From a77e630901fa31bc78961f325e2d990fc0d54cf5 Mon Sep 17 00:00:00 2001 From: Jon Evans Date: Sun, 30 Jul 2023 15:39:07 -0400 Subject: [PATCH] ADDED: Connectivity for graphic shapes on copper layers Graphic shapes (excluding text) can now have nets when on copper layers. Shapes behave like tracks in that they will pick up nets from connected pads, and follow track opacity settings. --- common/project/project_local_settings.cpp | 4 +- pcbnew/board_connected_item.h | 1 + pcbnew/connectivity/connectivity_algo.cpp | 46 +++-- pcbnew/connectivity/connectivity_items.cpp | 31 +++ pcbnew/connectivity/connectivity_items.h | 2 + .../dialog_graphic_item_properties.cpp | 35 ++++ .../dialog_graphic_item_properties_base.cpp | 11 +- .../dialog_graphic_item_properties_base.fbp | 131 ++++++++++++- .../dialog_graphic_item_properties_base.h | 5 +- .../drc_test_provider_copper_clearance.cpp | 124 +++++++++--- .../drc_test_provider_physical_clearance.cpp | 10 + pcbnew/netinfo_list.cpp | 12 ++ pcbnew/pcb_painter.cpp | 182 +++++++++++------- pcbnew/pcb_painter.h | 2 + pcbnew/pcb_shape.cpp | 110 ++++++++++- pcbnew/pcb_shape.h | 15 +- pcbnew/plot_brditems_plotter.cpp | 7 + pcbnew/plugins/kicad/pcb_parser.cpp | 11 +- pcbnew/plugins/kicad/pcb_plugin.cpp | 3 + pcbnew/plugins/kicad/pcb_plugin.h | 3 +- pcbnew/router/pns_kicad_iface.cpp | 5 +- pcbnew/router/pns_router.cpp | 3 - pcbnew/router/pns_solid.cpp | 11 ++ pcbnew/router/pns_solid.h | 16 +- pcbnew/router/pns_tool_base.cpp | 23 ++- pcbnew/tools/board_inspection_tool.cpp | 46 ++++- pcbnew/tools/pcb_control.cpp | 6 + pcbnew/tools/pcb_selection_tool.cpp | 46 ++++- pcbnew/widgets/appearance_controls.cpp | 10 +- pcbnew/zone_filler.cpp | 11 +- 30 files changed, 757 insertions(+), 165 deletions(-) diff --git a/common/project/project_local_settings.cpp b/common/project/project_local_settings.cpp index acc283538d..0b34328953 100644 --- a/common/project/project_local_settings.cpp +++ b/common/project/project_local_settings.cpp @@ -253,8 +253,8 @@ PROJECT_LOCAL_SETTINGS::PROJECT_LOCAL_SETTINGS( PROJECT* aProject, const wxStrin { if( At( ptr ).is_array() ) { - At( ptr ).push_back( LAYER_PADS ); - At( ptr ).push_back( LAYER_ZONES ); + At( ptr ).push_back( LAYER_PADS - GAL_LAYER_ID_START ); + At( ptr ).push_back( LAYER_ZONES - GAL_LAYER_ID_START ); } else { diff --git a/pcbnew/board_connected_item.h b/pcbnew/board_connected_item.h index 089b17cd8a..8d9dbefdb4 100644 --- a/pcbnew/board_connected_item.h +++ b/pcbnew/board_connected_item.h @@ -57,6 +57,7 @@ public: case PCB_ARC_T: case PCB_VIA_T: case PCB_ZONE_T: + case PCB_SHAPE_T: return true; default: diff --git a/pcbnew/connectivity/connectivity_algo.cpp b/pcbnew/connectivity/connectivity_algo.cpp index 39d2837cc6..732161a1e6 100644 --- a/pcbnew/connectivity/connectivity_algo.cpp +++ b/pcbnew/connectivity/connectivity_algo.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include @@ -59,25 +60,11 @@ bool CN_CONNECTIVITY_ALGO::Remove( BOARD_ITEM* aItem ) break; case PCB_PAD_T: - m_itemMap[aItem].MarkItemsAsInvalid(); - m_itemMap.erase( aItem ); - m_itemList.SetDirty( true ); - break; - case PCB_TRACE_T: case PCB_ARC_T: - m_itemMap[aItem].MarkItemsAsInvalid(); - m_itemMap.erase( aItem ); - m_itemList.SetDirty( true ); - break; - case PCB_VIA_T: - m_itemMap[aItem].MarkItemsAsInvalid(); - m_itemMap.erase( aItem ); - m_itemList.SetDirty( true ); - break; - case PCB_ZONE_T: + case PCB_SHAPE_T: m_itemMap[aItem].MarkItemsAsInvalid(); m_itemMap.erase ( aItem ); m_itemList.SetDirty( true ); @@ -189,6 +176,16 @@ bool CN_CONNECTIVITY_ALGO::Add( BOARD_ITEM* aItem ) add( m_itemList, static_cast( aItem ) ); break; + case PCB_SHAPE_T: + if( alreadyAdded( aItem ) ) + return false; + + if( !IsCopperLayer( aItem->GetLayer() ) ) + return false; + + add( m_itemList, static_cast( aItem ) ); + break; + case PCB_ZONE_T: { ZONE* zone = static_cast( aItem ); @@ -305,13 +302,15 @@ const CN_CONNECTIVITY_ALGO::CLUSTERS CN_CONNECTIVITY_ALGO::SearchClusters( CLUST if( aMode == CSM_PROPAGATE ) { return SearchClusters( aMode, - { PCB_TRACE_T, PCB_ARC_T, PCB_PAD_T, PCB_VIA_T, PCB_FOOTPRINT_T }, + { PCB_TRACE_T, PCB_ARC_T, PCB_PAD_T, PCB_VIA_T, PCB_FOOTPRINT_T, + PCB_SHAPE_T }, -1 ); } else { return SearchClusters( aMode, - { PCB_TRACE_T, PCB_ARC_T, PCB_PAD_T, PCB_VIA_T, PCB_ZONE_T, PCB_FOOTPRINT_T }, + { PCB_TRACE_T, PCB_ARC_T, PCB_PAD_T, PCB_VIA_T, PCB_ZONE_T, + PCB_FOOTPRINT_T, PCB_SHAPE_T }, -1 ); } } @@ -454,6 +453,7 @@ void CN_CONNECTIVITY_ALGO::Build( BOARD* aBoard, PROGRESS_REPORTER* aReporter ) size += zitems.size(); // Once for building RTrees size += zitems.size(); // Once for adding to connectivity size += aBoard->Tracks().size(); + size += aBoard->Drawings().size(); for( FOOTPRINT* footprint : aBoard->Footprints() ) size += footprint->Pads().size(); @@ -534,6 +534,17 @@ void CN_CONNECTIVITY_ALGO::Build( BOARD* aBoard, PROGRESS_REPORTER* aReporter ) } } + for( BOARD_ITEM* drawing : aBoard->Drawings() ) + { + if( PCB_SHAPE* shape = dynamic_cast( drawing ) ) + { + if( shape->IsOnCopperLayer() ) + Add( shape ); + } + + report( ++ii ); + } + if( aReporter ) { aReporter->SetCurrentProgress( (double) ii / (double) size ); @@ -553,6 +564,7 @@ void CN_CONNECTIVITY_ALGO::LocalBuild( const std::vector& aItems ) case PCB_VIA_T: case PCB_PAD_T: case PCB_FOOTPRINT_T: + case PCB_SHAPE_T: Add( item ); break; diff --git a/pcbnew/connectivity/connectivity_items.cpp b/pcbnew/connectivity/connectivity_items.cpp index 9d70dd5bdc..70c789c9f3 100644 --- a/pcbnew/connectivity/connectivity_items.cpp +++ b/pcbnew/connectivity/connectivity_items.cpp @@ -42,6 +42,10 @@ int CN_ITEM::AnchorCount() const case PCB_TRACE_T: case PCB_ARC_T: return 2; // start and end + + case PCB_SHAPE_T: + return m_anchors.size(); + default: return 1; } @@ -68,6 +72,9 @@ const VECTOR2I CN_ITEM::GetAnchor( int n ) const case PCB_VIA_T: return static_cast( m_parent )->GetStart(); + case PCB_SHAPE_T: + return ( n < static_cast( m_anchors.size() ) ) ? m_anchors[n]->Pos() : VECTOR2I(); + default: UNIMPLEMENTED_FOR( m_parent->GetClass() ); return VECTOR2I(); @@ -247,6 +254,21 @@ CN_ITEM* CN_LIST::Add( CN_ZONE_LAYER* zitem ) } +CN_ITEM* CN_LIST::Add( PCB_SHAPE* shape ) +{ + CN_ITEM* item = new CN_ITEM( shape, true ); + m_items.push_back( item ); + + for( const VECTOR2I& point : shape->GetConnectionPoints() ) + item->AddAnchor( point ); + + item->SetLayer( shape->GetLayer() ); + addItemtoTree( item ); + SetDirty(); + return item; +} + + void CN_LIST::RemoveInvalidItems( std::vector& aGarbage ) { if( !m_hasInvalid ) @@ -318,7 +340,16 @@ bool CN_ANCHOR::IsDangling() const return connected_count < minimal_count; if( Parent()->Type() == PCB_TRACE_T || Parent()->Type() == PCB_ARC_T ) + { accuracy = KiROUND( static_cast( Parent() )->GetWidth() / 2 ); + } + else if( Parent()->Type() == PCB_SHAPE_T ) + { + auto shape = static_cast( Parent() ); + + if( !shape->IsFilled() ) + accuracy = KiROUND( shape->GetWidth() / 2 ); + } // Items with multiple anchors have usually items connected to each anchor. // We want only the item count of this anchor point diff --git a/pcbnew/connectivity/connectivity_items.h b/pcbnew/connectivity/connectivity_items.h index 45e672d1f8..c6ae17e694 100644 --- a/pcbnew/connectivity/connectivity_items.h +++ b/pcbnew/connectivity/connectivity_items.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -423,6 +424,7 @@ public: CN_ITEM* Add( PCB_ARC* track ); CN_ITEM* Add( PCB_VIA* via ); CN_ITEM* Add( CN_ZONE_LAYER* zitem ); + CN_ITEM* Add( PCB_SHAPE* shape ); const std::vector Add( ZONE* zone, PCB_LAYER_ID aLayer ); diff --git a/pcbnew/dialogs/dialog_graphic_item_properties.cpp b/pcbnew/dialogs/dialog_graphic_item_properties.cpp index a86c75b4da..69c13d27e8 100644 --- a/pcbnew/dialogs/dialog_graphic_item_properties.cpp +++ b/pcbnew/dialogs/dialog_graphic_item_properties.cpp @@ -132,6 +132,33 @@ DIALOG_GRAPHIC_ITEM_PROPERTIES::DIALOG_GRAPHIC_ITEM_PROPERTIES( PCB_BASE_EDIT_FR m_LayerSelectionCtrl->SetBoardFrame( m_parent ); m_LayerSelectionCtrl->Resync(); + m_netSelector->SetBoard( aParent->GetBoard() ); + m_netSelector->SetNetInfo( &aParent->GetBoard()->GetNetInfo() ); + + int net = aShape->GetNetCode(); + + if( net >= 0 ) + { + m_netSelector->SetSelectedNetcode( net ); + } + else + { + m_netSelector->SetIndeterminateString( INDETERMINATE_STATE ); + m_netSelector->SetIndeterminate(); + } + + auto showHideNetInfo = + [&]() + { + m_netSelector->Show( aShape->IsOnCopperLayer() ); + m_netLabel->Show( aShape->IsOnCopperLayer() ); + }; + + showHideNetInfo(); + m_LayerSelectionCtrl->Bind( wxEVT_COMBOBOX, + [&]( wxCommandEvent& aEvt ) { showHideNetInfo() ;} ); + + SetInitialFocus( m_startXCtrl ); SetupStandardButtons(); @@ -199,6 +226,9 @@ bool DIALOG_GRAPHIC_ITEM_PROPERTIES::TransferDataToWindow() m_bezierCtrl2Y.Show( false ); } + m_netSelector->Show( m_item->IsOnCopperLayer() ); + m_netLabel->Show( m_item->IsOnCopperLayer() ); + // Change texts according to the segment shape: switch( m_item->GetShape() ) { @@ -381,6 +411,11 @@ bool DIALOG_GRAPHIC_ITEM_PROPERTIES::TransferDataFromWindow() m_item->RebuildBezierToSegmentsPointsList( m_item->GetWidth() ); + if( m_item->IsOnCopperLayer() ) + m_item->SetNetCode( m_netSelector->GetSelectedNetcode() ); + else + m_item->SetNetCode( -1 ); + commit.Push( _( "Modify drawing properties" ) ); // Notify clients which treat locked and unlocked items differently (ie: POINT_EDITOR) diff --git a/pcbnew/dialogs/dialog_graphic_item_properties_base.cpp b/pcbnew/dialogs/dialog_graphic_item_properties_base.cpp index 9702bf6c63..b6bb9ad670 100644 --- a/pcbnew/dialogs/dialog_graphic_item_properties_base.cpp +++ b/pcbnew/dialogs/dialog_graphic_item_properties_base.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b) +// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -183,7 +183,14 @@ DIALOG_GRAPHIC_ITEM_PROPERTIES_BASE::DIALOG_GRAPHIC_ITEM_PROPERTIES_BASE( wxWind gbSizer2->Add( m_LayerLabel, wxGBPosition( 5, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_LayerSelectionCtrl = new PCB_LAYER_BOX_SELECTOR( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - gbSizer2->Add( m_LayerSelectionCtrl, wxGBPosition( 5, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + gbSizer2->Add( m_LayerSelectionCtrl, wxGBPosition( 5, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxEXPAND, 5 ); + + m_netLabel = new wxStaticText( this, wxID_ANY, _("Net:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_netLabel->Wrap( -1 ); + gbSizer2->Add( m_netLabel, wxGBPosition( 6, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT, 5 ); + + m_netSelector = new NET_SELECTOR( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + gbSizer2->Add( m_netSelector, wxGBPosition( 6, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxEXPAND, 5 ); gbSizer2->AddGrowableCol( 1 ); diff --git a/pcbnew/dialogs/dialog_graphic_item_properties_base.fbp b/pcbnew/dialogs/dialog_graphic_item_properties_base.fbp index a6a8cfea71..916637e400 100644 --- a/pcbnew/dialogs/dialog_graphic_item_properties_base.fbp +++ b/pcbnew/dialogs/dialog_graphic_item_properties_base.fbp @@ -2654,7 +2654,7 @@ 5 1 1 - wxALIGN_CENTER_VERTICAL|wxEXPAND + wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxEXPAND 5 1 @@ -2718,6 +2718,135 @@ + + 5 + 1 + 0 + wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT + 6 + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Net: + 0 + + 0 + + + 0 + + 1 + m_netLabel + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + 1 + 1 + wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxEXPAND + 6 + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + NET_SELECTOR + 1 + + + 1 + + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + #include <widgets/net_selector.h> + + 0 + + + 0 + + 1 + m_netSelector + 1 + + + protected + 1 + + Resizable + + 1 + + ; ; forward_declare + 0 + + + + + + diff --git a/pcbnew/dialogs/dialog_graphic_item_properties_base.h b/pcbnew/dialogs/dialog_graphic_item_properties_base.h index c8ab103df4..e188b71adf 100644 --- a/pcbnew/dialogs/dialog_graphic_item_properties_base.h +++ b/pcbnew/dialogs/dialog_graphic_item_properties_base.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b) +// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -23,6 +23,7 @@ class PCB_LAYER_BOX_SELECTOR; #include #include #include +#include #include #include #include @@ -79,6 +80,8 @@ class DIALOG_GRAPHIC_ITEM_PROPERTIES_BASE : public DIALOG_SHIM wxBitmapComboBox* m_lineStyleCombo; wxStaticText* m_LayerLabel; PCB_LAYER_BOX_SELECTOR* m_LayerSelectionCtrl; + wxStaticText* m_netLabel; + NET_SELECTOR* m_netSelector; wxStdDialogButtonSizer* m_StandardButtonsSizer; wxButton* m_StandardButtonsSizerOK; wxButton* m_StandardButtonsSizerCancel; diff --git a/pcbnew/drc/drc_test_provider_copper_clearance.cpp b/pcbnew/drc/drc_test_provider_copper_clearance.cpp index 053655ce03..c0104d645f 100644 --- a/pcbnew/drc/drc_test_provider_copper_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_copper_clearance.cpp @@ -84,14 +84,14 @@ public: private: /** * Checks for track/via/hole <-> clearance - * @param track Track to text - * @param trackShape Primitive track shape + * @param item Track to text + * @param itemShape Primitive track shape * @param layer Which layer to test (in case of vias this can be multiple * @param other item against which to test the track item * @return false if there is a clearance violation reported, true if there is none */ - bool testTrackAgainstItem( PCB_TRACK* track, SHAPE* trackShape, PCB_LAYER_ID layer, - BOARD_ITEM* other ); + bool testSingleLayerItemAgainstItem( BOARD_CONNECTED_ITEM* item, SHAPE* itemShape, + PCB_LAYER_ID layer, BOARD_ITEM* other ); void testTrackClearances(); @@ -196,9 +196,10 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::Run() } -bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackAgainstItem( PCB_TRACK* track, SHAPE* trackShape, - PCB_LAYER_ID layer, - BOARD_ITEM* other ) +bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testSingleLayerItemAgainstItem( BOARD_CONNECTED_ITEM* item, + SHAPE* itemShape, + PCB_LAYER_ID layer, + BOARD_ITEM* other ) { bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ); bool testShorting = !m_drcEngine->IsErrorLimitExceeded( DRCE_SHORTING_ITEMS ); @@ -225,22 +226,25 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackAgainstItem( PCB_TRACK* track, if( testClearance || testShorting ) { - constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, track, other, layer ); + constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, item, other, layer ); clearance = constraint.GetValue().Min(); } if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 ) { // Special processing for track:track intersections - if( track->Type() == PCB_TRACE_T && other->Type() == PCB_TRACE_T ) + if( item->Type() == PCB_TRACE_T && other->Type() == PCB_TRACE_T ) { + PCB_TRACK* track = static_cast( item ); + PCB_TRACK* otherTrack = static_cast( other ); + SEG trackSeg( track->GetStart(), track->GetEnd() ); - SEG otherSeg( track->GetStart(), track->GetEnd() ); + SEG otherSeg( otherTrack->GetStart(), otherTrack->GetEnd() ); if( OPT_VECTOR2I intersection = trackSeg.Intersect( otherSeg ) ) { std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_TRACKS_CROSSING ); - drcItem->SetItems( track, other ); + drcItem->SetItems( item, other ); drcItem->SetViolatingRule( constraint.GetParentRule() ); reportViolation( drcItem, *intersection, layer ); @@ -249,9 +253,9 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackAgainstItem( PCB_TRACK* track, } } - if( trackShape->Collide( otherShape.get(), clearance - m_drcEpsilon, &actual, &pos ) ) + if( itemShape->Collide( otherShape.get(), clearance - m_drcEpsilon, &actual, &pos ) ) { - if( m_drcEngine->IsNetTieExclusion( track->GetNetCode(), layer, pos, other ) ) + if( m_drcEngine->IsNetTieExclusion( item->GetNetCode(), layer, pos, other ) ) { // Collision occurred as track was entering a pad marked as a net-tie. We // allow these. @@ -261,12 +265,11 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackAgainstItem( PCB_TRACK* track, std::shared_ptr drce = DRC_ITEM::Create( DRCE_SHORTING_ITEMS ); wxString msg; - msg.Printf( _( "(nets %s and %s)" ), - track->GetNetname(), + msg.Printf( _( "(nets %s and %s)" ), item->GetNetname(), static_cast( other )->GetNetname() ); drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg ); - drce->SetItems( track, other ); + drce->SetItems( item, other ); reportViolation( drce, pos, layer ); has_error = true; @@ -283,7 +286,7 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackAgainstItem( PCB_TRACK* track, actual ); drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg ); - drce->SetItems( track, other ); + drce->SetItems( item, other ); drce->SetViolatingRule( constraint.GetParentRule() ); reportViolation( drce, pos, layer ); @@ -295,11 +298,11 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackAgainstItem( PCB_TRACK* track, } } - if( testHoles && ( track->HasHole() || other->HasHole() ) ) + if( testHoles && ( item->HasHole() || other->HasHole() ) ) { - std::array a{ track, other }; - std::array b{ other, track }; - std::array a_shape{ trackShape, otherShape.get() }; + std::array a{ item, other }; + std::array b{ other, item }; + std::array a_shape{ itemShape, otherShape.get() }; for( size_t ii = 0; ii < 2; ++ii ) { @@ -637,7 +640,8 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackClearances() // If we get an error, mark the pair as having a clearance error already // Only continue if we are reporting all track errors - if( !testTrackAgainstItem( track, trackShape.get(), layer, other ) ) + if( !testSingleLayerItemAgainstItem( track, trackShape.get(), layer, + other ) ) { if( it != checkedPairs.end() ) it->second.has_error = true; @@ -720,8 +724,8 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testPadAgainstItem( PAD* pad, SHAPE* pa if( BOARD_CONNECTED_ITEM* connectedItem = dynamic_cast( other ) ) otherNet = connectedItem->GetNetCode(); - // Pads and vias of the same (defined) net get a waiver on clearance and hole tests - if( ( otherPad || otherVia ) && otherNet && otherNet == padNet ) + // Other objects of the same (defined) net get a waiver on clearance and hole tests + if( otherNet && otherNet == padNet ) { testClearance = testShorting = false; testHoles = false; @@ -1003,10 +1007,82 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testGraphicClearances( ) } }; + std::unordered_map checkedPairs; + + auto testCopperGraphic = + [&]( PCB_SHAPE* aShape ) + { + PCB_LAYER_ID layer = aShape->GetLayer(); + + m_board->m_CopperItemRTreeCache->QueryColliding( aShape, layer, layer, + // Filter: + [&]( BOARD_ITEM* other ) -> bool + { + auto otherCItem = dynamic_cast( other ); + + if( otherCItem && otherCItem->GetNetCode() == aShape->GetNetCode() ) + return false; + + // Pads and tracks handled separately + if( other->Type() == PCB_PAD_T || other->Type() == PCB_ARC_T || + other->Type() == PCB_TRACE_T || other->Type() == PCB_VIA_T ) + { + return false; + } + + BOARD_ITEM* a = aShape; + BOARD_ITEM* b = other; + + // store canonical order so we don't collide in both directions + // (a:b and b:a) + if( static_cast( a ) > static_cast( b ) ) + std::swap( a, b ); + + auto it = checkedPairs.find( { a, b } ); + + if( it != checkedPairs.end() && it->second.layers.test( layer ) ) + { + return false; + } + else + { + checkedPairs[ { a, b } ].layers.set( layer ); + return true; + } + }, + // Visitor: + [&]( BOARD_ITEM* other ) -> bool + { + BOARD_ITEM* a = aShape; + BOARD_ITEM* b = other; + + // store canonical order so we don't collide in both directions + // (a:b and b:a) + if( static_cast( a ) > static_cast( b ) ) + std::swap( a, b ); + + auto it = checkedPairs.find( { a, b } ); + + if( !testSingleLayerItemAgainstItem( aShape, + aShape->GetEffectiveShape().get(), + layer, other ) ) + { + if( it != checkedPairs.end() ) + it->second.has_error = true; + } + + return !m_drcEngine->IsCancelled(); + }, + m_board->m_DRCMaxClearance ); + }; + for( BOARD_ITEM* item : m_board->Drawings() ) { testGraphicAgainstZone( item ); + if( item->Type() == PCB_SHAPE_T && item->IsOnCopperLayer() ) + testCopperGraphic( static_cast( item ) ); + if( !reportProgress( ii++, (int) count, progressDelta ) ) return; } diff --git a/pcbnew/drc/drc_test_provider_physical_clearance.cpp b/pcbnew/drc/drc_test_provider_physical_clearance.cpp index b07accdf7e..5a8f70e903 100644 --- a/pcbnew/drc/drc_test_provider_physical_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_physical_clearance.cpp @@ -357,6 +357,16 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run() break; } + case SHAPE_T::SEGMENT: + { + SHAPE_LINE_CHAIN asPoly; + asPoly.Append( shape->GetStart() ); + asPoly.Append( shape->GetEnd() ); + + testShapeLineChain( asPoly, shape->GetWidth(), layer, item, c ); + break; + } + default: UNIMPLEMENTED_FOR( shape->SHAPE_T_asString() ); } diff --git a/pcbnew/netinfo_list.cpp b/pcbnew/netinfo_list.cpp index 4ca38753ab..621bbd9819 100644 --- a/pcbnew/netinfo_list.cpp +++ b/pcbnew/netinfo_list.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -226,6 +227,17 @@ void NETINFO_MAPPING::Update() for( PCB_TRACK* track : m_board->Tracks() ) nets.insert( track->GetNetCode() ); + for( BOARD_ITEM* item : m_board->Drawings() ) + { + if( item->Type() != PCB_SHAPE_T ) + continue; + + PCB_SHAPE* shape = static_cast( item ); + + if( shape->GetNetCode() > 0 ) + nets.insert( shape->GetNetCode() ); + } + // footprints/pads for( FOOTPRINT* footprint : m_board->Footprints() ) { diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index 8f0f9ca8d5..a18b42b3f3 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -461,6 +461,8 @@ COLOR4D PCB_RENDER_SETTINGS::GetColor( const VIEW_ITEM* aItem, int aLayer ) cons color.a *= m_zoneOpacity; else if( item->Type() == PCB_BITMAP_T ) color.a *= m_imageOpacity; + else if( item->Type() == PCB_SHAPE_T && item->IsOnCopperLayer() ) + color.a *= m_trackOpacity; if( item->GetForcedTransparency() > 0.0 ) color = color.WithAlpha( color.a * ( 1.0 - item->GetForcedTransparency() ) ); @@ -690,78 +692,8 @@ void PCB_PAINTER::draw( const PCB_TRACK* aTrack, int aLayer ) if( aTrack->GetNetCode() <= NETINFO_LIST::UNCONNECTED ) return; - // When drawing netnames, clip the track to the viewport - BOX2D viewport; - VECTOR2D screenSize = m_gal->GetScreenPixelSize(); - const MATRIX3x3D& matrix = m_gal->GetScreenWorldMatrix(); - - viewport.SetOrigin( VECTOR2D( matrix * VECTOR2D( 0, 0 ) ) ); - viewport.SetEnd( VECTOR2D( matrix * screenSize ) ); - viewport.Normalize(); - - BOX2I clipBox( viewport.GetOrigin(), viewport.GetSize() ); - SEG visibleSeg( start, end ); - - ClipLine( &clipBox, visibleSeg.A.x, visibleSeg.A.y, visibleSeg.B.x, visibleSeg.B.y ); - - wxString netName = aTrack->GetUnescapedShortNetname(); - size_t num_char = netName.size(); - - // Check if the track is long enough to have a netname displayed - int seg_minlength = track_width * num_char; - - if( visibleSeg.Length() < seg_minlength ) - return; - - double textSize = track_width; - double penWidth = textSize / 12.0; - EDA_ANGLE textOrientation; - int num_names = 1; - - if( end.y == start.y ) // horizontal - { - textOrientation = ANGLE_HORIZONTAL; - num_names = std::max( num_names, - static_cast( aTrack->GetLength() / viewport.GetWidth() ) ); - } - else if( end.x == start.x ) // vertical - { - textOrientation = ANGLE_VERTICAL; - num_names = std::max( num_names, - static_cast( aTrack->GetLength() / viewport.GetHeight() ) ); - } - else - { - textOrientation = -EDA_ANGLE( visibleSeg.B - visibleSeg.A ); - textOrientation.Normalize90(); - - double min_size = std::min( viewport.GetWidth(), viewport.GetHeight() ); - num_names = std::max( num_names, - static_cast( aTrack->GetLength() / ( M_SQRT2 * min_size ) ) ); - } - - m_gal->SetIsStroke( true ); - m_gal->SetIsFill( false ); - m_gal->SetStrokeColor( color ); - m_gal->SetLineWidth( penWidth ); - m_gal->SetFontBold( false ); - m_gal->SetFontItalic( false ); - m_gal->SetFontUnderlined( false ); - m_gal->SetTextMirrored( false ); - m_gal->SetGlyphSize( VECTOR2D( textSize * 0.55, textSize * 0.55 ) ); - m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER ); - m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER ); - - for( int ii = 0; ii < num_names; ++ii ) - { - VECTOR2I textPosition = - VECTOR2D( start ) * static_cast( num_names - ii ) / ( num_names + 1 ) - + VECTOR2D( end ) * static_cast( ii + 1 ) / ( num_names + 1 ); - - if( clipBox.Contains( textPosition ) ) - m_gal->BitmapText( netName, textPosition, textOrientation ); - } - + SHAPE_SEGMENT trackShape( { aTrack->GetStart(), aTrack->GetEnd() }, aTrack->GetWidth() ); + renderNetNameForSegment( trackShape, color, aTrack->GetUnescapedShortNetname() ); return; } else if( IsCopperLayer( aLayer ) || aLayer == LAYER_LOCKED_ITEM_SHADOW ) @@ -797,6 +729,85 @@ void PCB_PAINTER::draw( const PCB_TRACK* aTrack, int aLayer ) } +void PCB_PAINTER::renderNetNameForSegment( const SHAPE_SEGMENT& aSeg, const COLOR4D& aColor, + const wxString& aNetName ) const +{ + // When drawing netnames, clip the track to the viewport + BOX2D viewport; + VECTOR2D screenSize = m_gal->GetScreenPixelSize(); + const MATRIX3x3D& matrix = m_gal->GetScreenWorldMatrix(); + + viewport.SetOrigin( VECTOR2D( matrix * VECTOR2D( 0, 0 ) ) ); + viewport.SetEnd( VECTOR2D( matrix * screenSize ) ); + viewport.Normalize(); + + BOX2I clipBox( viewport.GetOrigin(), viewport.GetSize() ); + SEG visibleSeg( aSeg.GetSeg().A, aSeg.GetSeg().B ); + + ClipLine( &clipBox, visibleSeg.A.x, visibleSeg.A.y, visibleSeg.B.x, visibleSeg.B.y ); + + size_t num_char = aNetName.size(); + + // Check if the track is long enough to have a netname displayed + int seg_minlength = aSeg.GetWidth() * num_char; + + if( visibleSeg.Length() < seg_minlength ) + return; + + double textSize = aSeg.GetWidth(); + double penWidth = textSize / 12.0; + EDA_ANGLE textOrientation; + int num_names = 1; + + VECTOR2I start = aSeg.GetSeg().A; + VECTOR2I end = aSeg.GetSeg().B; + + if( end.y == start.y ) // horizontal + { + textOrientation = ANGLE_HORIZONTAL; + num_names = std::max( num_names, + static_cast( aSeg.GetSeg().Length() / viewport.GetWidth() ) ); + } + else if( end.x == start.x ) // vertical + { + textOrientation = ANGLE_VERTICAL; + num_names = std::max( num_names, + static_cast( aSeg.GetSeg().Length() / viewport.GetHeight() ) ); + } + else + { + textOrientation = -EDA_ANGLE( visibleSeg.B - visibleSeg.A ); + textOrientation.Normalize90(); + + double min_size = std::min( viewport.GetWidth(), viewport.GetHeight() ); + num_names = std::max( num_names, + static_cast( aSeg.GetSeg().Length() / ( M_SQRT2 * min_size ) ) ); + } + + m_gal->SetIsStroke( true ); + m_gal->SetIsFill( false ); + m_gal->SetStrokeColor( aColor ); + m_gal->SetLineWidth( penWidth ); + m_gal->SetFontBold( false ); + m_gal->SetFontItalic( false ); + m_gal->SetFontUnderlined( false ); + m_gal->SetTextMirrored( false ); + m_gal->SetGlyphSize( VECTOR2D( textSize * 0.55, textSize * 0.55 ) ); + m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER ); + m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER ); + + for( int ii = 0; ii < num_names; ++ii ) + { + VECTOR2I textPosition = + VECTOR2D( start ) * static_cast( num_names - ii ) / ( num_names + 1 ) + + VECTOR2D( end ) * static_cast( ii + 1 ) / ( num_names + 1 ); + + if( clipBox.Contains( textPosition ) ) + m_gal->BitmapText( aNetName, textPosition, textOrientation ); + } +} + + void PCB_PAINTER::draw( const PCB_ARC* aArc, int aLayer ) { VECTOR2D center( aArc->GetCenter() ); @@ -1666,11 +1677,36 @@ void PCB_PAINTER::draw( const PAD* aPad, int aLayer ) void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer ) { - COLOR4D color = m_pcbSettings.GetColor( aShape, aShape->GetLayer() ); + COLOR4D color = m_pcbSettings.GetColor( aShape, aLayer ); bool outline_mode = !viewer_settings()->m_ViewersDisplay.m_DisplayGraphicsFill; int thickness = getLineThickness( aShape->GetWidth() ); PLOT_DASH_TYPE lineStyle = aShape->GetStroke().GetPlotStyle(); + if( IsNetnameLayer( aLayer ) ) + { + if( !pcbconfig() || pcbconfig()->m_Display.m_NetNames < 2 ) + return; + + if( aShape->GetNetCode() <= NETINFO_LIST::UNCONNECTED ) + return; + + wxString netname = aShape->GetUnescapedShortNetname(); + + if( netname.IsEmpty() ) + return; + + if( aShape->GetShape() == SHAPE_T::SEGMENT ) + { + SHAPE_SEGMENT seg( { aShape->GetStart(), aShape->GetEnd() }, aShape->GetWidth() ); + renderNetNameForSegment( seg, color, netname ); + return; + } + + // TODO: Maybe use some of the pad code? + + return; + } + if( aLayer == LAYER_LOCKED_ITEM_SHADOW ) { color = m_pcbSettings.GetColor( aShape, aLayer ); diff --git a/pcbnew/pcb_painter.h b/pcbnew/pcb_painter.h index 3af55cb921..c8d772f830 100644 --- a/pcbnew/pcb_painter.h +++ b/pcbnew/pcb_painter.h @@ -217,6 +217,8 @@ protected: void strokeText( const wxString& aText, const VECTOR2I& aPosition, const TEXT_ATTRIBUTES& aAttrs, const KIFONT::METRICS& aFontMetrics ); + void renderNetNameForSegment( const SHAPE_SEGMENT& aSeg, const COLOR4D& aColor, const wxString& aNetName ) const; + protected: PCB_RENDER_SETTINGS m_pcbSettings; FRAME_T m_frameType; diff --git a/pcbnew/pcb_shape.cpp b/pcbnew/pcb_shape.cpp index b65dc9c3f1..47f82f7826 100644 --- a/pcbnew/pcb_shape.cpp +++ b/pcbnew/pcb_shape.cpp @@ -36,14 +36,14 @@ #include PCB_SHAPE::PCB_SHAPE( BOARD_ITEM* aParent, KICAD_T aItemType, SHAPE_T aShapeType ) : - BOARD_ITEM( aParent, aItemType ), + BOARD_CONNECTED_ITEM( aParent, aItemType ), EDA_SHAPE( aShapeType, pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH ), FILL_T::NO_FILL ) { } PCB_SHAPE::PCB_SHAPE( BOARD_ITEM* aParent, SHAPE_T shapetype ) : - BOARD_ITEM( aParent, PCB_SHAPE_T ), + BOARD_CONNECTED_ITEM( aParent, PCB_SHAPE_T ), EDA_SHAPE( shapetype, pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH ), FILL_T::NO_FILL ) { } @@ -86,6 +86,58 @@ bool PCB_SHAPE::IsType( const std::vector& aScanTypes ) const } +void PCB_SHAPE::SetLayer( PCB_LAYER_ID aLayer ) +{ + BOARD_ITEM::SetLayer( aLayer ); + + if( !IsOnCopperLayer() ) + SetNetCode( -1 ); +} + + +std::vector PCB_SHAPE::GetConnectionPoints() const +{ + std::vector ret; + + // For filled shapes, we may as well use a centroid + if( IsFilled() ) + { + ret.emplace_back( GetCenter() ); + return ret; + } + + switch( m_shape ) + { + case SHAPE_T::ARC: + ret.emplace_back( GetArcMid() ); + KI_FALLTHROUGH; + + case SHAPE_T::SEGMENT: + case SHAPE_T::BEZIER: + ret.emplace_back( GetStart() ); + ret.emplace_back( GetEnd() ); + break; + + case SHAPE_T::POLY: + for( auto iter = GetPolyShape().CIterate(); iter; ++iter ) + ret.emplace_back( *iter ); + + break; + + case SHAPE_T::RECTANGLE: + for( const VECTOR2I& pt : GetRectCorners() ) + ret.emplace_back( pt ); + + break; + + default: + break; + } + + return ret; +} + + void PCB_SHAPE::StyleFromSettings( const BOARD_DESIGN_SETTINGS& settings ) { m_stroke.SetWidth( settings.GetLineThickness( GetLayer() ) ); @@ -329,6 +381,25 @@ double PCB_SHAPE::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const } +void PCB_SHAPE::ViewGetLayers( int aLayers[], int& aCount ) const +{ + aLayers[0] = GetLayer(); + + if( IsOnCopperLayer() ) + { + aLayers[1] = GetNetnameLayer( aLayers[0] ); + aCount = 2; + } + else + { + aCount = 1; + } + + if( IsLocked() ) + aLayers[ aCount++ ] = LAYER_LOCKED_ITEM_SHADOW; +} + + void PCB_SHAPE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector& aList ) { if( aFrame->GetName() == PCB_EDIT_FRAME_NAME ) @@ -350,7 +421,15 @@ void PCB_SHAPE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector 0 ) + { + return wxString::Format( _( "%s %s on %s" ), GetFriendlyName(), GetNetnameMsg(), + GetLayerName() ); + } + else + { + return wxString::Format( _( "%s on %s" ), GetFriendlyName(), GetLayerName() ); + } } @@ -439,9 +518,9 @@ static struct PCB_SHAPE_DESC { PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance(); REGISTER_TYPE( PCB_SHAPE ); - propMgr.AddTypeCast( new TYPE_CAST ); + propMgr.AddTypeCast( new TYPE_CAST ); propMgr.AddTypeCast( new TYPE_CAST ); - propMgr.InheritsAfter( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( BOARD_ITEM ) ); + propMgr.InheritsAfter( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( BOARD_CONNECTED_ITEM ) ); propMgr.InheritsAfter( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ) ); // Need to initialise enum_map before we can use a Property enum for it @@ -455,10 +534,13 @@ static struct PCB_SHAPE_DESC layerEnum.Map( *seq, LSET::Name( *seq ) ); } - auto layerProperty = new PROPERTY_ENUM( - _HKI( "Layer" ), &PCB_SHAPE::SetLayer, &PCB_SHAPE::GetLayer ); + void ( PCB_SHAPE::*shapeLayerSetter )( PCB_LAYER_ID ) = &PCB_SHAPE::SetLayer; + PCB_LAYER_ID ( PCB_SHAPE::*shapeLayerGetter )() const = &PCB_SHAPE::GetLayer; - propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ), layerProperty ); + auto layerProperty = new PROPERTY_ENUM( + _HKI( "Layer" ), shapeLayerSetter, shapeLayerGetter ); + + propMgr.ReplaceProperty( TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Layer" ), layerProperty ); // Only polygons have meaningful Position properties. // On other shapes, these are duplicates of the Start properties. @@ -475,5 +557,17 @@ static struct PCB_SHAPE_DESC _HKI( "Position X" ), isPolygon ); propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( BOARD_ITEM ), _HKI( "Position Y" ), isPolygon ); + + auto isCopper = + []( INSPECTABLE* aItem ) -> bool + { + if( PCB_SHAPE* shape = dynamic_cast( aItem ) ) + return shape->IsOnCopperLayer(); + + return false; + }; + + propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( BOARD_CONNECTED_ITEM ), + _HKI( "Net" ), isCopper ); } } _PCB_SHAPE_DESC; diff --git a/pcbnew/pcb_shape.h b/pcbnew/pcb_shape.h index bb56ec2c65..28b9c52fad 100644 --- a/pcbnew/pcb_shape.h +++ b/pcbnew/pcb_shape.h @@ -25,7 +25,7 @@ #ifndef PCB_SHAPE_H #define PCB_SHAPE_H -#include +#include #include @@ -35,7 +35,7 @@ class FOOTPRINT; class MSG_PANEL_ITEM; -class PCB_SHAPE : public BOARD_ITEM, public EDA_SHAPE +class PCB_SHAPE : public BOARD_CONNECTED_ITEM, public EDA_SHAPE { public: PCB_SHAPE( BOARD_ITEM* aParent, KICAD_T aItemType, SHAPE_T aShapeType ); @@ -61,11 +61,20 @@ public: bool IsType( const std::vector& aScanTypes ) const override; + void SetLayer( PCB_LAYER_ID aLayer ) override; + PCB_LAYER_ID GetLayer() const override { return m_layer; } + void SetPosition( const VECTOR2I& aPos ) override { setPosition( aPos ); } VECTOR2I GetPosition() const override { return getPosition(); } VECTOR2I GetCenter() const override { return getCenter(); } + /** + * @return a list of connection points (may be empty): points where this shape can form + * electrical connections to other shapes that are natural "start/end" points. + */ + std::vector GetConnectionPoints() const; + bool HasLineStroke() const override { return true; } STROKE_PARAMS GetStroke() const override { return m_stroke; } @@ -139,6 +148,8 @@ public: virtual const BOX2I ViewBBox() const override; + virtual void ViewGetLayers( int aLayers[], int& aCount ) const override; + ///< @copydoc VIEW_ITEM::ViewGetLOD double ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const override; diff --git a/pcbnew/plot_brditems_plotter.cpp b/pcbnew/plot_brditems_plotter.cpp index 9d50914897..02258af583 100644 --- a/pcbnew/plot_brditems_plotter.cpp +++ b/pcbnew/plot_brditems_plotter.cpp @@ -714,6 +714,13 @@ void BRDITEMS_PLOTTER::PlotShape( const PCB_SHAPE* aShape ) gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_ETCHEDCMP ); gbr_metadata.SetCopper( true ); } + else if( aShape->GetNetCode() > 0 ) + { + gbr_metadata.SetCopper( true ); + gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR ); + gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_NET ); + gbr_metadata.SetNetName( aShape->GetNetname() ); + } else { // Graphic items (PCB_SHAPE, TEXT) having no net have the NonConductor attribute diff --git a/pcbnew/plugins/kicad/pcb_parser.cpp b/pcbnew/plugins/kicad/pcb_parser.cpp index f75ee4f6e7..0a1efb21cc 100644 --- a/pcbnew/plugins/kicad/pcb_parser.cpp +++ b/pcbnew/plugins/kicad/pcb_parser.cpp @@ -2851,8 +2851,17 @@ PCB_SHAPE* PCB_PARSER::parsePCB_SHAPE( BOARD_ITEM* aParent ) NeedRIGHT(); break; + case T_net: + if( !shape->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) ) + { + wxLogError( _( "Invalid net ID in\nfile: '%s'\nline: %d\noffset: %d." ), + CurSource(), CurLineNumber(), CurOffset() ); + } + NeedRIGHT(); + break; + default: - Expecting( "layer, width, fill, tstamp, locked or status" ); + Expecting( "layer, width, fill, tstamp, locked, net or status" ); } } diff --git a/pcbnew/plugins/kicad/pcb_plugin.cpp b/pcbnew/plugins/kicad/pcb_plugin.cpp index 9eabf3b363..705d9701ef 100644 --- a/pcbnew/plugins/kicad/pcb_plugin.cpp +++ b/pcbnew/plugins/kicad/pcb_plugin.cpp @@ -986,6 +986,9 @@ void PCB_PLUGIN::format( const PCB_SHAPE* aShape, int aNestLevel ) const formatLayer( aShape->GetLayer() ); + if( aShape->GetNetCode() > 0 ) + m_out->Print( 0, " (net %d)", m_mapping->Translate( aShape->GetNetCode() ) ); + m_out->Print( 0, " (tstamp %s)", TO_UTF8( aShape->m_Uuid.AsString() ) ); m_out->Print( 0, ")\n" ); diff --git a/pcbnew/plugins/kicad/pcb_plugin.h b/pcbnew/plugins/kicad/pcb_plugin.h index 7c5e5f0b9d..49d88a2a77 100644 --- a/pcbnew/plugins/kicad/pcb_plugin.h +++ b/pcbnew/plugins/kicad/pcb_plugin.h @@ -131,7 +131,8 @@ class PCB_PLUGIN; // forward decl //#define SEXPR_BOARD_FILE_VERSION 20221018 // Via & pad zone-layer-connections //#define SEXPR_BOARD_FILE_VERSION 20230410 // DNP attribute propagated from schematic to attr //#define SEXPR_BOARD_FILE_VERSION 20230517 // Teardrop parameters for pads and vias -#define SEXPR_BOARD_FILE_VERSION 20230620 // PCB Fields +//#define SEXPR_BOARD_FILE_VERSION 20230620 // PCB Fields +#define SEXPR_BOARD_FILE_VERSION 20230730 // Connectivity for graphic shapes #define BOARD_FILE_HOST_VERSION 20200825 ///< Earlier files than this include the host tag #define LEGACY_ARC_FORMATTING 20210925 ///< These were the last to use old arc formatting diff --git a/pcbnew/router/pns_kicad_iface.cpp b/pcbnew/router/pns_kicad_iface.cpp index 257762224d..c6c1375115 100644 --- a/pcbnew/router/pns_kicad_iface.cpp +++ b/pcbnew/router/pns_kicad_iface.cpp @@ -1363,15 +1363,14 @@ bool PNS_KICAD_IFACE_BASE::syncGraphicalItem( PNS::NODE* aWorld, PCB_SHAPE* aIte } } - solid->SetNet( -1 ); + solid->SetAnchorPoints( aItem->GetConnectionPoints() ); + solid->SetNet( aItem->GetNetCode() ); solid->SetParent( aItem ); solid->SetShape( shape ); // takes ownership if( shapes.size() > 1 ) solid->SetIsCompoundShapePrimitive(); - solid->SetRoutable( false ); - aWorld->Add( std::move( solid ) ); } diff --git a/pcbnew/router/pns_router.cpp b/pcbnew/router/pns_router.cpp index e1bc371dd9..0e814da255 100644 --- a/pcbnew/router/pns_router.cpp +++ b/pcbnew/router/pns_router.cpp @@ -292,9 +292,6 @@ bool ROUTER::isStartingPointRoutable( const VECTOR2I& aWhere, ITEM* aStartItem, failureReason = _( "Cannot start routing from a text item." ); break; - case PCB_SHAPE_T: - failureReason = _( "Cannot start routing from a graphic." ); - default: break; } diff --git a/pcbnew/router/pns_solid.cpp b/pcbnew/router/pns_solid.cpp index 8b0147846a..e9fba10479 100644 --- a/pcbnew/router/pns_solid.cpp +++ b/pcbnew/router/pns_solid.cpp @@ -92,4 +92,15 @@ void SOLID::SetPos( const VECTOR2I& aCenter ) } +VECTOR2I SOLID::Anchor( int aN ) const +{ + return m_anchorPoints.empty() ? m_pos : m_anchorPoints[aN]; +} + + int SOLID::AnchorCount() const +{ + return m_anchorPoints.empty() ? 1 : m_anchorPoints.size(); +} + + } diff --git a/pcbnew/router/pns_solid.h b/pcbnew/router/pns_solid.h index af28be298d..988c08387f 100644 --- a/pcbnew/router/pns_solid.h +++ b/pcbnew/router/pns_solid.h @@ -64,6 +64,7 @@ public: m_pos = aSolid.m_pos; m_padToDie = aSolid.m_padToDie; m_orientation = aSolid.m_orientation; + m_anchorPoints = aSolid.m_anchorPoints; } SOLID& operator=( const SOLID& aB ) @@ -77,6 +78,7 @@ public: m_pos = aB.m_pos; m_padToDie = aB.m_padToDie; m_orientation = aB.m_orientation; + m_anchorPoints = aB.m_anchorPoints; return *this; } @@ -105,15 +107,12 @@ public: int GetPadToDie() const { return m_padToDie; } void SetPadToDie( int aLen ) { m_padToDie = aLen; } - virtual VECTOR2I Anchor( int aN ) const override - { - return m_pos; - } + virtual VECTOR2I Anchor( int aN ) const override; - virtual int AnchorCount() const override - { - return 1; - } + virtual int AnchorCount() const override; + + const std::vector& AnchorPoints() const { return m_anchorPoints; } + void SetAnchorPoints( const std::vector& aPoints ) { m_anchorPoints = aPoints; } VECTOR2I Offset() const { return m_offset; } void SetOffset( const VECTOR2I& aOffset ) { m_offset = aOffset; } @@ -143,6 +142,7 @@ private: int m_padToDie; EDA_ANGLE m_orientation; HOLE* m_hole; + std::vector m_anchorPoints; }; } diff --git a/pcbnew/router/pns_tool_base.cpp b/pcbnew/router/pns_tool_base.cpp index fa309b6957..1dd8b95015 100644 --- a/pcbnew/router/pns_tool_base.cpp +++ b/pcbnew/router/pns_tool_base.cpp @@ -423,7 +423,28 @@ const VECTOR2I TOOL_BASE::snapToItem( ITEM* aItem, const VECTOR2I& aP ) switch( aItem->Kind() ) { case ITEM::SOLID_T: - return static_cast( aItem )->Pos(); + { + SOLID* solid = static_cast( aItem ); + + if( solid->AnchorPoints().empty() ) + return solid->Anchor( 0 ); + + VECTOR2I anchor; + SEG::ecoord minDist = std::numeric_limits::max(); + + for( VECTOR2I anchorCandidate : solid->AnchorPoints() ) + { + SEG::ecoord distSq = ( aP - anchorCandidate ).SquaredEuclideanNorm(); + + if( distSq < minDist ) + { + minDist = distSq; + anchor = anchorCandidate; + } + } + + return anchor; + } case ITEM::VIA_T: return static_cast( aItem )->Pos(); diff --git a/pcbnew/tools/board_inspection_tool.cpp b/pcbnew/tools/board_inspection_tool.cpp index 622b713acf..69ac24e8b2 100644 --- a/pcbnew/tools/board_inspection_tool.cpp +++ b/pcbnew/tools/board_inspection_tool.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -84,17 +85,45 @@ bool BOARD_INSPECTION_TOOL::Init() std::shared_ptr netSubMenu = std::make_shared(); netSubMenu->SetTool( this ); - static std::vector connectedTypes = { PCB_TRACE_T, - PCB_VIA_T, - PCB_ARC_T, - PCB_PAD_T, - PCB_ZONE_T }; + // Only show the net menu if all items in the selection are connectable + auto showNetMenuFunc = + []( const SELECTION& aSelection ) + { + if( aSelection.Empty() ) + return false; + + for( const EDA_ITEM* item : aSelection ) + { + switch( item->Type() ) + { + case PCB_TRACE_T: + case PCB_ARC_T: + case PCB_VIA_T: + case PCB_PAD_T: + case PCB_ZONE_T: + continue; + + case PCB_SHAPE_T: + { + if( !static_cast( item )->IsOnCopperLayer() ) + return false; + else + continue; + } + + default: + return false; + } + } + + return true; + }; CONDITIONAL_MENU& menu = selectionTool->GetToolMenu().GetMenu(); selectionTool->GetToolMenu().RegisterSubMenu( netSubMenu ); - menu.AddMenu( netSubMenu.get(), SELECTION_CONDITIONS::OnlyTypes( connectedTypes ), 100 ); + menu.AddMenu( netSubMenu.get(), showNetMenuFunc, 100 ); return true; } @@ -1616,7 +1645,7 @@ int BOARD_INSPECTION_TOOL::HighlightItem( const TOOL_EVENT& aEvent ) guide.SetPreferredLayer( activeLayer ); GENERAL_COLLECTOR collector; - collector.Collect( board, { PCB_PAD_T, PCB_VIA_T, PCB_TRACE_T, PCB_ARC_T }, aPosition, + collector.Collect( board, { PCB_PAD_T, PCB_VIA_T, PCB_TRACE_T, PCB_ARC_T, PCB_SHAPE_T }, aPosition, guide ); if( collector.GetCount() == 0 ) @@ -1935,7 +1964,8 @@ void BOARD_INSPECTION_TOOL::calculateSelectionRatsnest( const VECTOR2I& aDelta ) || aItem->Type() == PCB_ARC_T || aItem->Type() == PCB_ZONE_T || aItem->Type() == PCB_FOOTPRINT_T - || aItem->Type() == PCB_VIA_T ); + || aItem->Type() == PCB_VIA_T + || aItem->Type() == PCB_SHAPE_T ); } ) ) { return; diff --git a/pcbnew/tools/pcb_control.cpp b/pcbnew/tools/pcb_control.cpp index 64404c636b..5cd09aa8ac 100644 --- a/pcbnew/tools/pcb_control.cpp +++ b/pcbnew/tools/pcb_control.cpp @@ -153,6 +153,12 @@ int PCB_CONTROL::TrackDisplayMode( const TOOL_EVENT& aEvent ) view()->Update( track, KIGFX::REPAINT ); } + for( BOARD_ITEM* shape : board()->Drawings() ) + { + if( shape->Type() == PCB_SHAPE_T && static_cast( shape )->IsOnCopperLayer() ) + view()->Update( shape, KIGFX::REPAINT ); + } + canvas()->Refresh(); return 0; diff --git a/pcbnew/tools/pcb_selection_tool.cpp b/pcbnew/tools/pcb_selection_tool.cpp index 77a0502750..90202fd0a2 100644 --- a/pcbnew/tools/pcb_selection_tool.cpp +++ b/pcbnew/tools/pcb_selection_tool.cpp @@ -1258,6 +1258,7 @@ void PCB_SELECTION_TOOL::selectAllConnectedTracks( std::map> trackMap; std::map viaMap; std::map padMap; + std::map> shapeMap; std::set startPadSet; std::vector cleanupItems; std::vector> activePts; @@ -1275,7 +1276,7 @@ void PCB_SELECTION_TOOL::selectAllConnectedTracks( continue; auto connectedItems = connectivity->GetConnectedItems( startItem, - { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T }, true ); + { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, PCB_SHAPE_T }, true ); // Build maps of connected items for( BOARD_CONNECTED_ITEM* item : connectedItems ) @@ -1305,6 +1306,16 @@ void PCB_SELECTION_TOOL::selectAllConnectedTracks( break; } + case PCB_SHAPE_T: + { + PCB_SHAPE* shape = static_cast( item ); + + for( const auto& point : shape->GetConnectionPoints() ) + shapeMap[point].push_back( shape ); + + break; + } + default: break; } @@ -1331,6 +1342,14 @@ void PCB_SELECTION_TOOL::selectAllConnectedTracks( activePts.push_back( { startItem->GetPosition(), startItem->GetLayerSet() } ); break; + case PCB_SHAPE_T: + { + PCB_SHAPE* shape = static_cast( startItem ); + + for( const auto& point : shape->GetConnectionPoints() ) + activePts.push_back( { point, startItem->GetLayerSet() } ); + } + default: break; } @@ -1424,6 +1443,31 @@ void PCB_SELECTION_TOOL::selectAllConnectedTracks( } } + for( PCB_SHAPE* shape : shapeMap[pt] ) + { + if( !layerSetCu.Contains( shape->GetLayer() ) ) + continue; + + if( !shape->IsSelected() ) + select( shape ); + + if( !shape->HasFlag( SKIP_STRUCT ) ) + { + shape->SetFlags( SKIP_STRUCT ); + cleanupItems.push_back( shape ); + + for( const VECTOR2I& newPoint : shape->GetConnectionPoints() ) + { + if( newPoint == pt ) + continue; + + activePts.push_back( { newPoint, shape->GetLayerSet() } ); + } + + expand = true; + } + } + if( viaMap.count( pt ) ) { PCB_VIA* via = viaMap[pt]; diff --git a/pcbnew/widgets/appearance_controls.cpp b/pcbnew/widgets/appearance_controls.cpp index 17e5dddac9..2013d447c8 100644 --- a/pcbnew/widgets/appearance_controls.cpp +++ b/pcbnew/widgets/appearance_controls.cpp @@ -2947,11 +2947,11 @@ void APPEARANCE_CONTROLS::onObjectOpacitySlider( int aLayer, float aOpacity ) switch( aLayer ) { - case static_cast( LAYER_TRACKS ): options.m_TrackOpacity = aOpacity; break; - case static_cast( LAYER_VIAS ): options.m_ViaOpacity = aOpacity; break; - case static_cast( LAYER_PADS ): options.m_PadOpacity = aOpacity; break; - case static_cast( LAYER_ZONES ): options.m_ZoneOpacity = aOpacity; break; - case static_cast( LAYER_DRAW_BITMAPS ): options.m_ImageOpacity = aOpacity; break; + case static_cast( LAYER_TRACKS ): options.m_TrackOpacity = aOpacity; break; + case static_cast( LAYER_VIAS ): options.m_ViaOpacity = aOpacity; break; + case static_cast( LAYER_PADS ): options.m_PadOpacity = aOpacity; break; + case static_cast( LAYER_ZONES ): options.m_ZoneOpacity = aOpacity; break; + case static_cast( LAYER_DRAW_BITMAPS ): options.m_ImageOpacity = aOpacity; break; default: return; } diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp index 2aa9344056..b53097c0fd 100644 --- a/pcbnew/zone_filler.cpp +++ b/pcbnew/zone_filler.cpp @@ -1108,12 +1108,14 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa knockoutTrackClearance( track ); } - // Add graphic item clearances. They are by definition unconnected, and have no clearance - // definitions of their own. + // Add graphic item clearances. // auto knockoutGraphicClearance = [&]( BOARD_ITEM* aItem ) { + int shapeNet = ( aItem->Type() == PCB_SHAPE_T ) ? static_cast( aItem )->GetNetCode() : -1; + bool sameNet = shapeNet == aZone->GetNetCode() && aZone->GetNetCode() != 0; + // A item on the Edge_Cuts or Margin is always seen as on any layer: if( aItem->IsOnLayer( aLayer ) || aItem->IsOnLayer( Edge_Cuts ) @@ -1125,7 +1127,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa int gap = evalRulesForItems( PHYSICAL_CLEARANCE_CONSTRAINT, aZone, aItem, aLayer ); - if( aItem->IsOnLayer( aLayer ) ) + if( aItem->IsOnLayer( aLayer ) && !sameNet ) { gap = std::max( gap, evalRulesForItems( CLEARANCE_CONSTRAINT, aZone, aItem, aLayer ) ); @@ -1142,7 +1144,8 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa aZone, aItem, Margin ) ); } - addKnockout( aItem, aLayer, gap + extra_margin, ignoreLineWidths, aHoles ); + if( gap > 0 ) + addKnockout( aItem, aLayer, gap + extra_margin, ignoreLineWidths, aHoles ); } } };