Add DRC checks for dangling vias and tracks.

Fixes https://gitlab.com/kicad/code/kicad/issues/1999
This commit is contained in:
Jeff Young 2020-05-14 20:24:10 +01:00
parent 3b84653591
commit aedc624340
8 changed files with 102 additions and 88 deletions

View File

@ -97,14 +97,14 @@ public:
bds.m_DRCSeverities[ DRCE_OVERLAPPING_FOOTPRINTS ] = RPT_SEVERITY_IGNORE;
}
bds.m_DRCSeverities[ DRCE_SHORT ] = RPT_SEVERITY_ACTION;
bds.m_DRCSeverities[ DRCE_REDUNDANT_VIA ] = RPT_SEVERITY_ACTION;
bds.m_DRCSeverities[ DRCE_DUPLICATE_TRACK ] = RPT_SEVERITY_ACTION;
bds.m_DRCSeverities[ DRCE_MERGE_TRACKS ] = RPT_SEVERITY_ACTION;
bds.m_DRCSeverities[ DRCE_DANGLING_TRACK ] = RPT_SEVERITY_ACTION;
bds.m_DRCSeverities[ DRCE_DANGLING_VIA ] = RPT_SEVERITY_ACTION;
bds.m_DRCSeverities[ DRCE_ZERO_LENGTH_TRACK ] = RPT_SEVERITY_ACTION;
bds.m_DRCSeverities[ DRCE_TRACK_IN_PAD ] = RPT_SEVERITY_ACTION;
bds.m_DRCSeverities[ CLEANUP_SHORT ] = RPT_SEVERITY_ACTION;
bds.m_DRCSeverities[ CLEANUP_REDUNDANT_VIA ] = RPT_SEVERITY_ACTION;
bds.m_DRCSeverities[ CLEANUP_DUPLICATE_TRACK ] = RPT_SEVERITY_ACTION;
bds.m_DRCSeverities[ CLEANUP_MERGE_TRACKS ] = RPT_SEVERITY_ACTION;
bds.m_DRCSeverities[ CLEANUP_DANGLING_TRACK ] = RPT_SEVERITY_ACTION;
bds.m_DRCSeverities[ CLEANUP_DANGLING_VIA ] = RPT_SEVERITY_ACTION;
bds.m_DRCSeverities[ CLEANUP_ZERO_LENGTH_TRACK ] = RPT_SEVERITY_ACTION;
bds.m_DRCSeverities[ CLEANUP_TRACK_IN_PAD ] = RPT_SEVERITY_ACTION;
DRC_ITEM drc( 0 );
wxString severity;

View File

@ -598,6 +598,35 @@ void CONNECTIVITY_DATA::GetUnconnectedEdges( std::vector<CN_EDGE>& aEdges) const
}
bool CONNECTIVITY_DATA::TestTrackEndpointDangling( TRACK* aTrack, wxPoint* aPos )
{
auto items = GetConnectivityAlgo()->ItemEntry( aTrack ).GetItems();
// Not in the connectivity system. This is a bug!
if( items.empty() )
{
wxFAIL_MSG( "track not in connectivity system" );
return false;
}
CN_ITEM* citem = items.front();
if( !citem->Valid() )
return false;
for( const std::shared_ptr<CN_ANCHOR>& anchor : citem->Anchors() )
{
if( anchor->IsDangling() )
{
*aPos = (wxPoint) anchor->Pos();
return true;
}
}
return false;
}
const std::vector<BOARD_CONNECTED_ITEM*> CONNECTIVITY_DATA::GetConnectedItems(
const BOARD_CONNECTED_ITEM* aItem, const VECTOR2I& aAnchor, KICAD_T aTypes[] )
{
@ -700,3 +729,5 @@ const std::vector<CN_EDGE> CONNECTIVITY_DATA::GetRatsnestForComponent( MODULE* a
return edges;
}

View File

@ -183,6 +183,8 @@ public:
void GetUnconnectedEdges( std::vector<CN_EDGE>& aEdges ) const;
bool TestTrackEndpointDangling( TRACK* aTrack, wxPoint* aPos );
/**
* Function ClearDynamicRatsnest()
* Erases the temporary dynamic ratsnest (i.e. the ratsnest lines that

View File

@ -868,12 +868,11 @@ void DRC::testDrilledHoles()
void DRC::testTracks( wxWindow *aActiveWindow, bool aShowProgressBar )
{
wxProgressDialog * progressDialog = NULL;
const int delta = 500; // This is the number of tests between 2 calls to the
// progress bar
int count = m_pcb->Tracks().size();
int deltamax = count/delta;
wxProgressDialog* progressDialog = NULL;
const int delta = 500; // This is the number of tests between 2 calls to the
// progress bar
int count = m_pcb->Tracks().size();
int deltamax = count/delta;
if( aShowProgressBar && deltamax > 3 )
{
@ -885,6 +884,17 @@ void DRC::testTracks( wxWindow *aActiveWindow, bool aShowProgressBar )
progressDialog->Update( 0, wxEmptyString );
}
std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_pcb->GetConnectivity();
BOARD_DESIGN_SETTINGS& settings = m_pcb->GetDesignSettings();
if( !m_pcb->GetDesignSettings().Ignore( DRCE_DANGLING_TRACK )
|| !m_pcb->GetDesignSettings().Ignore( DRCE_DANGLING_VIA ) )
{
connectivity->Clear();
connectivity->Build( m_pcb ); // just in case. This really needs to be reliable.
}
int ii = 0;
count = 0;
@ -909,6 +919,19 @@ void DRC::testTracks( wxWindow *aActiveWindow, bool aShowProgressBar )
// Test new segment against tracks and pads, optionally against copper zones
doTrackDrc( *seg_it, seg_it + 1, m_pcb->Tracks().end(), m_doZonesTest );
// Test for dangling items
int code = (*seg_it)->Type() == PCB_VIA_T ? DRCE_DANGLING_VIA : DRCE_DANGLING_TRACK;
wxPoint pos;
if( !settings.Ignore( code ) && connectivity->TestTrackEndpointDangling( *seg_it, &pos ) )
{
DRC_ITEM* drcItem = new DRC_ITEM( code );
drcItem->SetItems( *seg_it );
MARKER_PCB* marker = new MARKER_PCB( drcItem, pos );
addMarkerToPcb( marker );
}
}
if( progressDialog )

View File

@ -56,6 +56,8 @@ enum PCB_DRC_CODE {
DRCE_ZONES_INTERSECT, ///< copper area outlines intersect
DRCE_ZONES_TOO_CLOSE, ///< copper area outlines are too close
DRCE_ZONE_HAS_EMPTY_NET, ///< copper area has a net but no pads in nets, which is suspicious
DRCE_DANGLING_VIA, ///< via which isn't connected to anything
DRCE_DANGLING_TRACK, ///< track with at least one end not connected to anything
DRCE_HOLE_NEAR_PAD, ///< hole too close to pad
DRCE_HOLE_NEAR_TRACK, ///< hole too close to track
DRCE_DRILLED_HOLES_TOO_CLOSE, ///< overlapping drilled holes break drill bits
@ -96,14 +98,14 @@ enum PCB_DRC_CODE {
DRCE_LAST = DRCE_UNRESOLVED_VARIABLE,
// These are actually Cleanup Tracks and Vias actions, not DRCE errors
DRCE_SHORT,
DRCE_REDUNDANT_VIA,
DRCE_DUPLICATE_TRACK,
DRCE_MERGE_TRACKS,
DRCE_DANGLING_TRACK,
DRCE_DANGLING_VIA,
DRCE_ZERO_LENGTH_TRACK,
DRCE_TRACK_IN_PAD
CLEANUP_SHORT,
CLEANUP_REDUNDANT_VIA,
CLEANUP_DUPLICATE_TRACK,
CLEANUP_MERGE_TRACKS,
CLEANUP_DANGLING_TRACK,
CLEANUP_DANGLING_VIA,
CLEANUP_ZERO_LENGTH_TRACK,
CLEANUP_TRACK_IN_PAD
};

View File

@ -78,9 +78,9 @@ wxString DRC_ITEM::GetErrorText( int aCode, bool aTranslate ) const
case DRCE_DISABLED_LAYER_ITEM: msg = _HKI( "Item on a disabled layer" ); break;
case DRCE_ZONES_INTERSECT: msg = _HKI( "Copper areas intersect" ); break;
case DRCE_ZONES_TOO_CLOSE: msg = _HKI( "Copper areas too close" ); break;
case DRCE_ZONE_HAS_EMPTY_NET: msg = _HKI( "Copper zone net has no pads" ); break;
case DRCE_DANGLING_VIA: msg = _HKI( "Via is not connected" ); break;
case DRCE_DANGLING_TRACK: msg = _HKI( "Track has unconnected end" ); break;
case DRCE_HOLE_NEAR_PAD: msg = _HKI( "Hole too close to pad" ); break;
case DRCE_HOLE_NEAR_TRACK: msg = _HKI( "Hole too close to track" ); break;
case DRCE_TOO_SMALL_TRACK_WIDTH: msg = _HKI( "Track width too small" ); break;
@ -121,14 +121,14 @@ wxString DRC_ITEM::GetErrorText( int aCode, bool aTranslate ) const
case DRCE_EXTRA_FOOTPRINT: msg = _HKI( "Extra footprint" ); break;
// For cleanup tracks and vias:
case DRCE_SHORT: msg = _HKI( "Remove track shorting two nets" ); break;
case DRCE_REDUNDANT_VIA: msg = _HKI( "Remove redundant via" ); break;
case DRCE_DUPLICATE_TRACK: msg = _HKI( "Remove duplicate track" ); break;
case DRCE_MERGE_TRACKS: msg = _HKI( "Merge co-linear tracks" ); break;
case DRCE_DANGLING_TRACK: msg = _HKI( "Remove dangling track" ); break;
case DRCE_DANGLING_VIA: msg = _HKI( "Remove dangling via" ); break;
case DRCE_ZERO_LENGTH_TRACK: msg = _HKI( "Remove zero-length track" ); break;
case DRCE_TRACK_IN_PAD: msg = _HKI( "Remove track inside pad" ); break;
case CLEANUP_SHORT: msg = _HKI( "Remove track shorting two nets" ); break;
case CLEANUP_REDUNDANT_VIA: msg = _HKI( "Remove redundant via" ); break;
case CLEANUP_DUPLICATE_TRACK: msg = _HKI( "Remove duplicate track" ); break;
case CLEANUP_MERGE_TRACKS: msg = _HKI( "Merge co-linear tracks" ); break;
case CLEANUP_DANGLING_TRACK: msg = _HKI( "Remove dangling track" ); break;
case CLEANUP_DANGLING_VIA: msg = _HKI( "Remove dangling via" ); break;
case CLEANUP_ZERO_LENGTH_TRACK: msg = _HKI( "Remove zero-length track" ); break;
case CLEANUP_TRACK_IN_PAD: msg = _HKI( "Remove track inside pad" ); break;
case DRCE_UNRESOLVED_VARIABLE: msg = _HKI( "Unresolved text variable" ); break;

View File

@ -133,7 +133,7 @@ bool TRACKS_CLEANER::removeBadTrackSegments()
{
if( segment->GetNetCode() != testedPad->GetNetCode() )
{
DRC_ITEM* item = new DRC_ITEM( DRCE_SHORT );
DRC_ITEM* item = new DRC_ITEM( CLEANUP_SHORT );
item->SetItems( segment );
m_itemsList->push_back( item );
@ -145,7 +145,7 @@ bool TRACKS_CLEANER::removeBadTrackSegments()
{
if( segment->GetNetCode() != testedTrack->GetNetCode() && !testedTrack->GetState( FLAG0 ) )
{
DRC_ITEM* item = new DRC_ITEM( DRCE_SHORT );
DRC_ITEM* item = new DRC_ITEM( CLEANUP_SHORT );
item->SetItems( segment );
m_itemsList->push_back( item );
@ -190,7 +190,7 @@ bool TRACKS_CLEANER::cleanupVias()
if( ( pad->GetLayerSet() & all_cu ) == all_cu )
{
DRC_ITEM* item = new DRC_ITEM( DRCE_REDUNDANT_VIA );
DRC_ITEM* item = new DRC_ITEM( CLEANUP_REDUNDANT_VIA );
item->SetItems( via1, pad );
m_itemsList->push_back( item );
@ -209,7 +209,7 @@ bool TRACKS_CLEANER::cleanupVias()
if( via1->GetViaType() == via2->GetViaType() )
{
DRC_ITEM* item = new DRC_ITEM( DRCE_REDUNDANT_VIA );
DRC_ITEM* item = new DRC_ITEM( CLEANUP_REDUNDANT_VIA );
item->SetItems( via1, via2 );
m_itemsList->push_back( item );
@ -224,35 +224,6 @@ bool TRACKS_CLEANER::cleanupVias()
}
bool TRACKS_CLEANER::testTrackEndpointDangling( TRACK* aTrack )
{
auto connectivity = m_brd->GetConnectivity();
auto items = connectivity->GetConnectivityAlgo()->ItemEntry( aTrack ).GetItems();
// Not in the connectivity system. This is a bug!
if( items.empty() )
{
wxASSERT( !items.empty() );
return false;
}
auto citem = items.front();
if( !citem->Valid() )
return false;
auto anchors = citem->Anchors();
for( const auto& anchor : anchors )
{
if( anchor->IsDangling() )
return true;
}
return false;
}
bool TRACKS_CLEANER::testTrackEndpointIsNode( TRACK* aTrack, bool aTstStart )
{
// A node is a point where more than 2 items are connected.
@ -299,15 +270,16 @@ bool TRACKS_CLEANER::deleteDanglingTracks()
for( TRACK* track : m_brd->Tracks() )
{
bool flag_erase = false; // Start without a good reason to erase it
bool flag_erase = false; // Start without a good reason to erase it
wxPoint pos;
// Tst if a track (or a via) endpoint is not connected to another track or to a zone.
if( testTrackEndpointDangling( track ) )
if( m_brd->GetConnectivity()->TestTrackEndpointDangling( track, &pos ) )
flag_erase = true;
if( flag_erase )
{
int errorCode = track->IsTrack() ? DRCE_DANGLING_TRACK : DRCE_DANGLING_VIA;
int errorCode = track->IsTrack() ? CLEANUP_DANGLING_TRACK : CLEANUP_DANGLING_VIA;
DRC_ITEM* item = new DRC_ITEM( errorCode );
item->SetItems( track );
m_itemsList->push_back( item );
@ -341,7 +313,7 @@ bool TRACKS_CLEANER::deleteNullSegments( TRACKS& aTracks )
{
if( segment->IsNull() && segment->Type() == PCB_TRACE_T && !segment->IsLocked() )
{
DRC_ITEM* item = new DRC_ITEM( DRCE_ZERO_LENGTH_TRACK );
DRC_ITEM* item = new DRC_ITEM( CLEANUP_ZERO_LENGTH_TRACK );
item->SetItems( segment );
m_itemsList->push_back( item );
@ -367,7 +339,7 @@ bool TRACKS_CLEANER::deleteTracksInPads()
{
if( pad->HitTest( track->GetStart() ) && pad->HitTest( track->GetEnd() ) )
{
DRC_ITEM* item = new DRC_ITEM( DRCE_TRACK_IN_PAD );
DRC_ITEM* item = new DRC_ITEM( CLEANUP_TRACK_IN_PAD );
item->SetItems( track );
m_itemsList->push_back( item );
@ -410,7 +382,7 @@ bool TRACKS_CLEANER::cleanupSegments()
&& track1->GetWidth() == track2->GetWidth()
&& track1->GetLayer() == track2->GetLayer() )
{
DRC_ITEM* item = new DRC_ITEM( DRCE_DUPLICATE_TRACK );
DRC_ITEM* item = new DRC_ITEM( CLEANUP_DUPLICATE_TRACK );
item->SetItems( track2 );
m_itemsList->push_back( item );
@ -510,7 +482,7 @@ bool TRACKS_CLEANER::mergeCollinearSegments( TRACK* aSeg1, TRACK* aSeg2 )
return false;
}
DRC_ITEM* item = new DRC_ITEM( DRCE_MERGE_TRACKS );
DRC_ITEM* item = new DRC_ITEM( CLEANUP_MERGE_TRACKS );
item->SetItems( aSeg1, aSeg2 );
m_itemsList->push_back( item );

View File

@ -80,13 +80,6 @@ private:
*/
bool cleanupSegments();
/**
* helper function
* Rebuild list of tracks, and connected tracks
* this info must be rebuilt when tracks are erased
*/
void buildTrackConnectionInfo();
/**
* helper function
* merge aTrackRef and aCandidate, when possible,
@ -97,15 +90,6 @@ private:
*/
bool mergeCollinearSegments( TRACK* aSeg1, TRACK* aSeg2 );
/**
* @return true if aTrack has at least one end dangling, i.e. connected
* to nothing.
* if aTrack is a via, it is dangling if the via is connected to nothing
* or only one item.
* @param aTrack is the track (or the via) to test.
*/
bool testTrackEndpointDangling( TRACK* aTrack );
/**
* @return true if a track end position is a node, i.e. a end connected
* to more than one item.