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.
This commit is contained in:
Jon Evans 2023-07-30 15:39:07 -04:00
parent 812143ac69
commit a77e630901
30 changed files with 757 additions and 165 deletions

View File

@ -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
{

View File

@ -57,6 +57,7 @@ public:
case PCB_ARC_T:
case PCB_VIA_T:
case PCB_ZONE_T:
case PCB_SHAPE_T:
return true;
default:

View File

@ -34,6 +34,7 @@
#include <geometry/geometry_utils.h>
#include <board_commit.h>
#include <thread_pool.h>
#include <pcb_shape.h>
#include <wx/log.h>
@ -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<PCB_VIA*>( aItem ) );
break;
case PCB_SHAPE_T:
if( alreadyAdded( aItem ) )
return false;
if( !IsCopperLayer( aItem->GetLayer() ) )
return false;
add( m_itemList, static_cast<PCB_SHAPE*>( aItem ) );
break;
case PCB_ZONE_T:
{
ZONE* zone = static_cast<ZONE*>( 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<PCB_SHAPE*>( 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<BOARD_ITEM*>& aItems )
case PCB_VIA_T:
case PCB_PAD_T:
case PCB_FOOTPRINT_T:
case PCB_SHAPE_T:
Add( item );
break;

View File

@ -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<const PCB_VIA*>( m_parent )->GetStart();
case PCB_SHAPE_T:
return ( n < static_cast<int>( 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<CN_ITEM*>& 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<const PCB_TRACK*>( Parent() )->GetWidth() / 2 );
}
else if( Parent()->Type() == PCB_SHAPE_T )
{
auto shape = static_cast<const PCB_SHAPE*>( 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

View File

@ -33,6 +33,7 @@
#include <pad.h>
#include <footprint.h>
#include <pcb_track.h>
#include <pcb_shape.h>
#include <zone.h>
#include <geometry/shape_poly_set.h>
@ -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<CN_ITEM*> Add( ZONE* zone, PCB_LAYER_ID aLayer );

View File

@ -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)

View File

@ -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 );

View File

@ -2654,7 +2654,7 @@
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">1</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxEXPAND</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxEXPAND</property>
<property name="row">5</property>
<property name="rowspan">1</property>
<object class="wxBitmapComboBox" expanded="1">
@ -2718,6 +2718,135 @@
<property name="window_style"></property>
</object>
</object>
<object class="gbsizeritem" expanded="1">
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">0</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT</property>
<property name="row">6</property>
<property name="rowspan">1</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Net:</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_netLabel</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="gbsizeritem" expanded="1">
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">1</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxEXPAND</property>
<property name="row">6</property>
<property name="rowspan">1</property>
<object class="CustomControl" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="class">NET_SELECTOR</property>
<property name="close_button">1</property>
<property name="construction"></property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="declaration"></property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="include">#include &lt;widgets/net_selector.h&gt;</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_netSelector</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="settings"></property>
<property name="show">1</property>
<property name="size"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
</object>
</object>
</object>

View File

@ -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 <wx/gbsizer.h>
#include <wx/checkbox.h>
#include <wx/bmpcbox.h>
#include <widgets/net_selector.h>
#include <wx/sizer.h>
#include <wx/button.h>
#include <wx/dialog.h>
@ -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;

View File

@ -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<PCB_TRACK*>( item );
PCB_TRACK* otherTrack = static_cast<PCB_TRACK*>( 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<DRC_ITEM> 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<DRC_ITEM> 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<BOARD_CONNECTED_ITEM*>( 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<BOARD_ITEM*, 2> a{ track, other };
std::array<BOARD_ITEM*, 2> b{ other, track };
std::array<SHAPE*, 2> a_shape{ trackShape, otherShape.get() };
std::array<BOARD_ITEM*, 2> a{ item, other };
std::array<BOARD_ITEM*, 2> b{ other, item };
std::array<SHAPE*, 2> 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<BOARD_CONNECTED_ITEM*>( 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<PTR_PTR_CACHE_KEY, layers_checked> 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<BOARD_CONNECTED_ITEM*>( 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<void*>( a ) > static_cast<void*>( 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<void*>( a ) > static_cast<void*>( 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<PCB_SHAPE*>( item ) );
if( !reportProgress( ii++, (int) count, progressDelta ) )
return;
}

View File

@ -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() );
}

View File

@ -25,6 +25,7 @@
#include <footprint.h>
#include <macros.h>
#include <pad.h>
#include <pcb_shape.h>
#include <pcb_track.h>
#include <zone.h>
#include <netinfo.h>
@ -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<PCB_SHAPE*>( item );
if( shape->GetNetCode() > 0 )
nets.insert( shape->GetNetCode() );
}
// footprints/pads
for( FOOTPRINT* footprint : m_board->Footprints() )
{

View File

@ -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<int>( aTrack->GetLength() / viewport.GetWidth() ) );
}
else if( end.x == start.x ) // vertical
{
textOrientation = ANGLE_VERTICAL;
num_names = std::max( num_names,
static_cast<int>( 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<int>( 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<double>( num_names - ii ) / ( num_names + 1 )
+ VECTOR2D( end ) * static_cast<double>( 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<int>( aSeg.GetSeg().Length() / viewport.GetWidth() ) );
}
else if( end.x == start.x ) // vertical
{
textOrientation = ANGLE_VERTICAL;
num_names = std::max( num_names,
static_cast<int>( 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<int>( 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<double>( num_names - ii ) / ( num_names + 1 )
+ VECTOR2D( end ) * static_cast<double>( 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 );

View File

@ -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;

View File

@ -36,14 +36,14 @@
#include <pcb_painter.h>
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<KICAD_T>& aScanTypes ) const
}
void PCB_SHAPE::SetLayer( PCB_LAYER_ID aLayer )
{
BOARD_ITEM::SetLayer( aLayer );
if( !IsOnCopperLayer() )
SetNetCode( -1 );
}
std::vector<VECTOR2I> PCB_SHAPE::GetConnectionPoints() const
{
std::vector<VECTOR2I> 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<MSG_PANEL_ITEM>& aList )
{
if( aFrame->GetName() == PCB_EDIT_FRAME_NAME )
@ -350,7 +421,15 @@ void PCB_SHAPE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_I
wxString PCB_SHAPE::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
{
return wxString::Format( _( "%s on %s" ), GetFriendlyName(), GetLayerName() );
if( GetNetCode() > 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<PCB_SHAPE, BOARD_ITEM> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_SHAPE, BOARD_CONNECTED_ITEM> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_SHAPE, EDA_SHAPE> );
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<PCB_SHAPE, PCB_LAYER_ID, BOARD_ITEM>(
_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<PCB_SHAPE, PCB_LAYER_ID>(
_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<PCB_SHAPE*>( aItem ) )
return shape->IsOnCopperLayer();
return false;
};
propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( BOARD_CONNECTED_ITEM ),
_HKI( "Net" ), isCopper );
}
} _PCB_SHAPE_DESC;

View File

@ -25,7 +25,7 @@
#ifndef PCB_SHAPE_H
#define PCB_SHAPE_H
#include <board_item.h>
#include <board_connected_item.h>
#include <eda_shape.h>
@ -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<KICAD_T>& 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<VECTOR2I> 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;

View File

@ -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

View File

@ -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" );
}
}

View File

@ -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" );

View File

@ -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

View File

@ -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 ) );
}

View File

@ -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;
}

View File

@ -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();
}
}

View File

@ -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<VECTOR2I>& AnchorPoints() const { return m_anchorPoints; }
void SetAnchorPoints( const std::vector<VECTOR2I>& 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<VECTOR2I> m_anchorPoints;
};
}

View File

@ -423,7 +423,28 @@ const VECTOR2I TOOL_BASE::snapToItem( ITEM* aItem, const VECTOR2I& aP )
switch( aItem->Kind() )
{
case ITEM::SOLID_T:
return static_cast<SOLID*>( aItem )->Pos();
{
SOLID* solid = static_cast<SOLID*>( aItem );
if( solid->AnchorPoints().empty() )
return solid->Anchor( 0 );
VECTOR2I anchor;
SEG::ecoord minDist = std::numeric_limits<SEG::ecoord>::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<VIA*>( aItem )->Pos();

View File

@ -38,6 +38,7 @@
#include <string_utils.h>
#include <tools/board_inspection_tool.h>
#include <fp_lib_table.h>
#include <pcb_shape.h>
#include <pcbnew_settings.h>
#include <widgets/appearance_controls.h>
#include <widgets/wx_html_report_box.h>
@ -84,17 +85,45 @@ bool BOARD_INSPECTION_TOOL::Init()
std::shared_ptr<NET_CONTEXT_MENU> netSubMenu = std::make_shared<NET_CONTEXT_MENU>();
netSubMenu->SetTool( this );
static std::vector<KICAD_T> 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<const PCB_SHAPE*>( 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;

View File

@ -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<PCB_SHAPE*>( shape )->IsOnCopperLayer() )
view()->Update( shape, KIGFX::REPAINT );
}
canvas()->Refresh();
return 0;

View File

@ -1258,6 +1258,7 @@ void PCB_SELECTION_TOOL::selectAllConnectedTracks(
std::map<VECTOR2I, std::vector<PCB_TRACK*>> trackMap;
std::map<VECTOR2I, PCB_VIA*> viaMap;
std::map<VECTOR2I, PAD*> padMap;
std::map<VECTOR2I, std::vector<PCB_SHAPE*>> shapeMap;
std::set<PAD*> startPadSet;
std::vector<BOARD_CONNECTED_ITEM*> cleanupItems;
std::vector<std::pair<VECTOR2I, LSET>> 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<PCB_SHAPE*>( 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<PCB_SHAPE*>( 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];

View File

@ -2947,11 +2947,11 @@ void APPEARANCE_CONTROLS::onObjectOpacitySlider( int aLayer, float aOpacity )
switch( aLayer )
{
case static_cast<int>( LAYER_TRACKS ): options.m_TrackOpacity = aOpacity; break;
case static_cast<int>( LAYER_VIAS ): options.m_ViaOpacity = aOpacity; break;
case static_cast<int>( LAYER_PADS ): options.m_PadOpacity = aOpacity; break;
case static_cast<int>( LAYER_ZONES ): options.m_ZoneOpacity = aOpacity; break;
case static_cast<int>( LAYER_DRAW_BITMAPS ): options.m_ImageOpacity = aOpacity; break;
case static_cast<int>( LAYER_TRACKS ): options.m_TrackOpacity = aOpacity; break;
case static_cast<int>( LAYER_VIAS ): options.m_ViaOpacity = aOpacity; break;
case static_cast<int>( LAYER_PADS ): options.m_PadOpacity = aOpacity; break;
case static_cast<int>( LAYER_ZONES ): options.m_ZoneOpacity = aOpacity; break;
case static_cast<int>( LAYER_DRAW_BITMAPS ): options.m_ImageOpacity = aOpacity; break;
default: return;
}

View File

@ -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<PCB_SHAPE*>( 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 );
}
}
};