Fix broken TRACKS_CLEANER algo to merge collinear track segments.
This commit is contained in:
parent
71aafb6eb3
commit
c9ccd8a642
|
@ -285,6 +285,30 @@ bool CN_ANCHOR::IsDangling() const
|
|||
}
|
||||
|
||||
|
||||
int CN_ANCHOR::ConnectedItemsCount() const
|
||||
{
|
||||
if( !m_cluster )
|
||||
return 0;
|
||||
|
||||
int connected_count = 0;
|
||||
|
||||
for( auto item : m_item->ConnectedItems() )
|
||||
{
|
||||
if( item->Parent()->Type() == PCB_ZONE_AREA_T )
|
||||
{
|
||||
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item->Parent() );
|
||||
|
||||
if( zone->HitTestFilledArea( wxPoint( Pos().x, Pos().y ) ) )
|
||||
connected_count++;
|
||||
}
|
||||
else if( item->Parent()->HitTest( wxPoint( Pos().x, Pos().y ) ) )
|
||||
connected_count++;
|
||||
}
|
||||
|
||||
return connected_count;
|
||||
}
|
||||
|
||||
|
||||
CN_CLUSTER::CN_CLUSTER()
|
||||
{
|
||||
m_items.reserve( 64 );
|
||||
|
|
|
@ -124,6 +124,12 @@ public:
|
|||
*/
|
||||
bool IsDangling() const;
|
||||
|
||||
/**
|
||||
* has meaning only for tracks and vias.
|
||||
* @return the count of items connected to this anchor
|
||||
*/
|
||||
int ConnectedItemsCount() const;
|
||||
|
||||
// Tag used for unconnected items.
|
||||
static const int TAG_UNCONNECTED = -1;
|
||||
|
||||
|
|
|
@ -252,6 +252,39 @@ bool TRACKS_CLEANER::testTrackEndpointDangling( TRACK* aTrack )
|
|||
}
|
||||
|
||||
|
||||
bool TRACKS_CLEANER::testTrackEndpointIsNode( TRACK* aTrack, bool aTstStart )
|
||||
{
|
||||
// A node is a point where more than 2 items are connected.
|
||||
|
||||
auto connectivity = m_brd->GetConnectivity();
|
||||
auto items = connectivity->GetConnectivityAlgo()->ItemEntry( aTrack ).GetItems();
|
||||
|
||||
if( items.empty() )
|
||||
return false;
|
||||
|
||||
auto citem = items.front();
|
||||
|
||||
if( !citem->Valid() )
|
||||
return false;
|
||||
|
||||
auto anchors = citem->Anchors();
|
||||
|
||||
VECTOR2I refpoint = aTstStart ? aTrack->GetStart() : aTrack->GetEnd();
|
||||
|
||||
for( const auto& anchor : anchors )
|
||||
{
|
||||
if( anchor->Pos() != refpoint )
|
||||
continue;
|
||||
|
||||
// The right anchor point is found: if more than one other item
|
||||
// (pad, via, track...) is connected, it is a node:
|
||||
return anchor->ConnectedItemsCount() > 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool TRACKS_CLEANER::deleteDanglingTracks()
|
||||
{
|
||||
bool item_erased = false;
|
||||
|
@ -362,12 +395,13 @@ bool TRACKS_CLEANER::cleanupSegments()
|
|||
|
||||
std::set<BOARD_ITEM*> toRemove;
|
||||
|
||||
// Remove duplicate segments (2 superimposed identical segments):
|
||||
for( auto it = m_brd->Tracks().begin(); it != m_brd->Tracks().end(); it++ )
|
||||
{
|
||||
auto track1 = *it;
|
||||
|
||||
if( track1->Type() != PCB_TRACE_T || ( track1->GetFlags() & IS_DELETED )
|
||||
|| track1->IsLocked() )
|
||||
|| track1->IsLocked() )
|
||||
continue;
|
||||
|
||||
for( auto it2 = it + 1; it2 != m_brd->Tracks().end(); it2++ )
|
||||
|
@ -400,6 +434,13 @@ bool TRACKS_CLEANER::cleanupSegments()
|
|||
for( auto track_it = m_brd->Tracks().begin(); track_it != m_brd->Tracks().end(); track_it++ )
|
||||
{
|
||||
auto segment = *track_it;
|
||||
|
||||
if( segment->Type() != PCB_TRACE_T ) // one can merge only track collinear segments, not vias.
|
||||
continue;
|
||||
|
||||
if( segment->GetFlags() & IS_DELETED ) // already taken in account
|
||||
continue;
|
||||
|
||||
auto connectivity = m_brd->GetConnectivity();
|
||||
|
||||
auto& entry = connectivity->GetConnectivityAlgo()->ItemEntry( segment );
|
||||
|
@ -408,10 +449,21 @@ bool TRACKS_CLEANER::cleanupSegments()
|
|||
{
|
||||
for( auto connected : citem->ConnectedItems() )
|
||||
{
|
||||
if( connected->Valid() && connected->Parent()->Type() == PCB_TRACE_T
|
||||
&& !( connected->Parent()->GetFlags() & IS_DELETED ) )
|
||||
if( !connected->Valid() || connected->Parent()->Type() != PCB_TRACE_T ||
|
||||
( connected->Parent()->GetFlags() & IS_DELETED ) )
|
||||
continue;
|
||||
|
||||
if( !( connected->Parent()->GetFlags() & IS_DELETED ) )
|
||||
{
|
||||
if( segment->ApproxCollinear( *static_cast<TRACK*>( connected->Parent() ) ) )
|
||||
TRACK* candidate = static_cast<TRACK*>( connected->Parent() );
|
||||
|
||||
if( !candidate->IsOnLayer( segment->GetLayer() ) )
|
||||
continue;
|
||||
|
||||
if( candidate->GetWidth() != segment->GetWidth() )
|
||||
continue;
|
||||
|
||||
if( segment->ApproxCollinear( *candidate ) )
|
||||
{
|
||||
modified |= mergeCollinearSegments(
|
||||
segment, static_cast<TRACK*>( connected->Parent() ) );
|
||||
|
@ -432,6 +484,45 @@ bool TRACKS_CLEANER::mergeCollinearSegments( TRACK* aSeg1, TRACK* aSeg2 )
|
|||
|
||||
auto connectivity = m_brd->GetConnectivity();
|
||||
|
||||
// Verify the removed point after merging is not a node.
|
||||
// If it is a node (i.e. if more than one other item is connected, the segments cannot be merged
|
||||
TRACK dummy_seg( *aSeg1 );
|
||||
|
||||
// Calculate the new ends of the segment to merge, and store them to dummy_seg:
|
||||
int min_x = std::min( aSeg1->GetStart().x,
|
||||
std::min( aSeg1->GetEnd().x, std::min( aSeg2->GetStart().x, aSeg2->GetEnd().x ) ) );
|
||||
int min_y = std::min( aSeg1->GetStart().y,
|
||||
std::min( aSeg1->GetEnd().y, std::min( aSeg2->GetStart().y, aSeg2->GetEnd().y ) ) );
|
||||
int max_x = std::max( aSeg1->GetStart().x,
|
||||
std::max( aSeg1->GetEnd().x, std::max( aSeg2->GetStart().x, aSeg2->GetEnd().x ) ) );
|
||||
int max_y = std::max( aSeg1->GetStart().y,
|
||||
std::max( aSeg1->GetEnd().y, std::max( aSeg2->GetStart().y, aSeg2->GetEnd().y ) ) );
|
||||
|
||||
if( ( aSeg1->GetStart().x > aSeg1->GetEnd().x )
|
||||
== ( aSeg1->GetStart().y > aSeg1->GetEnd().y ) )
|
||||
{
|
||||
dummy_seg.SetStart( wxPoint( min_x, min_y ) );
|
||||
dummy_seg.SetEnd( wxPoint( max_x, max_y ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
dummy_seg.SetStart( wxPoint( min_x, max_y ) );
|
||||
dummy_seg.SetEnd( wxPoint( max_x, min_y ) );
|
||||
}
|
||||
|
||||
// Now find the removed end(s) and stop merging if it is a node:
|
||||
if( aSeg1->GetStart() != dummy_seg.GetStart() && aSeg1->GetStart() != dummy_seg.GetEnd() )
|
||||
{
|
||||
if( testTrackEndpointIsNode( aSeg1, true ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( aSeg1->GetEnd() != dummy_seg.GetStart() && aSeg1->GetEnd() != dummy_seg.GetEnd() )
|
||||
{
|
||||
if( testTrackEndpointIsNode( aSeg1, false ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( m_itemsList )
|
||||
{
|
||||
m_itemsList->emplace_back( new DRC_ITEM( m_units, DRCE_MERGE_TRACKS,
|
||||
|
@ -442,27 +533,7 @@ bool TRACKS_CLEANER::mergeCollinearSegments( TRACK* aSeg1, TRACK* aSeg2 )
|
|||
if( !m_dryRun )
|
||||
{
|
||||
m_commit.Modify( aSeg1 );
|
||||
|
||||
int min_x = std::min( aSeg1->GetStart().x,
|
||||
std::min( aSeg1->GetEnd().x, std::min( aSeg2->GetStart().x, aSeg2->GetEnd().x ) ) );
|
||||
int min_y = std::min( aSeg1->GetStart().y,
|
||||
std::min( aSeg1->GetEnd().y, std::min( aSeg2->GetStart().y, aSeg2->GetEnd().y ) ) );
|
||||
int max_x = std::max( aSeg1->GetStart().x,
|
||||
std::max( aSeg1->GetEnd().x, std::max( aSeg2->GetStart().x, aSeg2->GetEnd().x ) ) );
|
||||
int max_y = std::max( aSeg1->GetStart().y,
|
||||
std::max( aSeg1->GetEnd().y, std::max( aSeg2->GetStart().y, aSeg2->GetEnd().y ) ) );
|
||||
|
||||
if( ( aSeg1->GetStart().x > aSeg1->GetEnd().x )
|
||||
== ( aSeg1->GetStart().y > aSeg1->GetEnd().y ) )
|
||||
{
|
||||
aSeg1->SetStart( wxPoint( min_x, min_y ) );
|
||||
aSeg1->SetEnd( wxPoint( max_x, max_y ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
aSeg1->SetStart( wxPoint( min_x, max_y ) );
|
||||
aSeg1->SetEnd( wxPoint( max_x, min_y ) );
|
||||
}
|
||||
*aSeg1 = dummy_seg;
|
||||
|
||||
connectivity->Update( aSeg1 );
|
||||
|
||||
|
|
|
@ -94,8 +94,24 @@ 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.
|
||||
* @param aTrack is the track to test.
|
||||
* @param aTstStart = true ot test the start point of the track, and false to
|
||||
* test the end point
|
||||
*/
|
||||
bool testTrackEndpointIsNode( TRACK* aTrack, bool aTstStart );
|
||||
|
||||
EDA_UNITS_T m_units;
|
||||
BOARD* m_brd;
|
||||
BOARD_COMMIT& m_commit;
|
||||
|
|
Loading…
Reference in New Issue