214
bool TRACKS_CLEANER::remove_duplicates_of_via( const VIA *aVia )
216
bool modified = false;
218
// Search and delete others vias at same location
220
for( VIA* alt_via = GetFirstVia( aVia->Next() ); alt_via != NULL;
223
next_via = GetFirstVia( alt_via->Next() );
225
if( (alt_via->GetViaType() == VIA_THROUGH) &&
226
(alt_via->GetStart() == aVia->GetStart()) )
229
m_Brd->GetRatsnest()->Remove( alt_via );
230
alt_via->ViewRelease();
231
alt_via->DeleteStructure();
212
238
bool TRACKS_CLEANER::clean_vias()
215
240
bool modified = false;
217
for( TRACK* track = m_Brd->m_Track; track; track = track->Next() )
219
// Correct via m_End defects (if any)
220
if( track->Type() == PCB_VIA_T )
222
if( track->GetStart() != track->GetEnd() )
223
track->SetEnd( track->GetStart() );
226
if( track->GetShape() != VIA_THROUGH )
229
// Search and delete others vias at same location
230
TRACK* alt_track = track->Next();
232
for( ; alt_track != NULL; alt_track = next_track )
234
next_track = alt_track->Next();
236
if( alt_track->GetShape() != VIA_THROUGH )
239
if( alt_track->GetStart() != track->GetStart() )
249
// Delete Via on pads at same location
250
for( TRACK* track = m_Brd->m_Track; track != NULL; track = next_track )
252
next_track = track->Next();
254
if( track->GetShape() != VIA_THROUGH )
257
// Examine the list of connected pads:
258
// if one pad through is found, the via can be removed
259
for( unsigned ii = 0; ii < track->m_PadsConnected.size(); ii++ )
261
D_PAD * pad = track->m_PadsConnected[ii];
263
if( (pad->GetLayerMask() & ALL_CU_LAYERS) == ALL_CU_LAYERS )
242
for( VIA* via = GetFirstVia( m_Brd->m_Track ); via != NULL;
243
via = GetFirstVia( via->Next() ) )
245
// Correct via m_End defects (if any), should never happen
246
if( via->GetStart() != via->GetEnd() )
248
wxFAIL_MSG( wxT( "Via with mismatching ends" ) );
249
via->SetEnd( via->GetStart() );
252
/* Important: these cleanups only do thru hole vias, they don't
253
* (yet) handle high density interconnects */
254
if( via->GetViaType() != VIA_THROUGH )
256
modified |= remove_duplicates_of_via( via );
258
/* To delete through Via on THT pads at same location
259
* Examine the list of connected pads:
260
* if one through pad is found, the via can be removed */
261
for( unsigned ii = 0; ii < via->m_PadsConnected.size(); ++ii )
265
// redundant: via delete it
263
const D_PAD *pad = via->m_PadsConnected[ii];
265
const LSET all_cu = LSET::AllCuMask();
267
if( (pad->GetLayerSet() & all_cu) == all_cu )
269
// redundant: delete the via
270
m_Brd->GetRatsnest()->Remove( via );
272
via->DeleteStructure();
288
353
bool modified = false;
289
bool item_erased = true;
290
while( item_erased ) // Iterate when at least one track is deleted
355
do // Iterate when at least one track is deleted
292
357
item_erased = false;
293
358
TRACK* next_track;
294
for( TRACK * track = m_Brd->m_Track; track ; track = next_track )
359
for( TRACK *track = m_Brd->m_Track; track != NULL; track = next_track )
296
361
next_track = track->Next();
298
int flag_erase = 0; //Not connected indicator
301
if( track->GetState( START_ON_PAD ) )
302
type_end |= START_ON_PAD;
304
if( track->GetState( END_ON_PAD ) )
305
type_end |= END_ON_PAD;
307
// if the track start point is not connected to a pad,
308
// test if this track start point is connected to another track
309
// For via test, an enhancement could be to test if connected
310
// to 2 items on different layers.
311
// Currently a via must be connected to 2 items, that can be on the same layer
312
LAYER_NUM top_layer, bottom_layer;
313
ZONE_CONTAINER* zone;
315
if( (type_end & START_ON_PAD ) == 0 )
317
TRACK* other = track->GetTrace( m_Brd->m_Track, NULL, FLG_START );
319
if( other == NULL ) // Test a connection to zones
321
if( track->Type() != PCB_VIA_T )
323
zone = m_Brd->HitTestForAnyFilledArea( track->GetStart(),
326
track->GetNetCode() );
330
((SEGVIA*)track)->LayerPair( &top_layer, &bottom_layer );
331
zone = m_Brd->HitTestForAnyFilledArea( track->GetStart(),
332
top_layer, bottom_layer,
333
track->GetNetCode() );
337
if( (other == NULL) && (zone == NULL) )
341
else // segment, via or zone connected to this end
343
track->start = other;
344
// If a via is connected to this end,
345
// test if this via has a second item connected.
346
// If no, remove it with the current segment
348
if( other && other->Type() == PCB_VIA_T )
350
// search for another segment following the via
351
track->SetState( BUSY, true );
353
SEGVIA* via = (SEGVIA*) other;
354
other = via->GetTrace( m_Brd->m_Track, NULL, FLG_START );
358
via->LayerPair( &top_layer, &bottom_layer );
359
zone = m_Brd->HitTestForAnyFilledArea( via->GetStart(),
365
if( (other == NULL) && (zone == NULL) )
368
track->SetState( BUSY, false );
373
// if track end point is not connected to a pad,
374
// test if this track end point is connected to an other track
375
if( (type_end & END_ON_PAD ) == 0 )
377
TRACK* other = track->GetTrace( m_Brd->m_Track, NULL, FLG_END );
379
if( other == NULL ) // Test a connection to zones
381
if( track->Type() != PCB_VIA_T )
383
zone = m_Brd->HitTestForAnyFilledArea( track->GetEnd(),
386
track->GetNetCode() );
390
((SEGVIA*)track)->LayerPair( &top_layer, &bottom_layer );
391
zone = m_Brd->HitTestForAnyFilledArea( track->GetEnd(),
392
top_layer, bottom_layer,
393
track->GetNetCode() );
397
if ( (other == NULL) && (zone == NULL) )
401
else // segment, via or zone connected to this end
405
// If a via is connected to this end, test if this via has a second item connected
406
// if no, remove it with the current segment
408
if( other && other->Type() == PCB_VIA_T )
410
// search for another segment following the via
412
track->SetState( BUSY, true );
414
SEGVIA* via = (SEGVIA*) other;
415
other = via->GetTrace( m_Brd->m_Track, NULL, FLG_END );
419
via->LayerPair( &top_layer, &bottom_layer );
420
zone = m_Brd->HitTestForAnyFilledArea( via->GetEnd(),
421
bottom_layer, top_layer,
425
if( (other == NULL) && (zone == NULL) )
428
track->SetState( BUSY, false );
363
bool flag_erase = false; // Start without a good reason to erase it
365
/* if a track endpoint is not connected to a pad, test if
366
* the endpoint is connected to another track or to a zone.
367
* For via test, an enhancement could be to test if
368
* connected to 2 items on different layers. Currently
369
* a via must be connected to 2 items, that can be on the
372
// Check if there is nothing attached on the start
373
if( !(track->GetState( START_ON_PAD )) )
374
flag_erase |= testTrackEndpointDangling( track, ENDPOINT_START );
376
// Check if there is nothing attached on the end
377
if( !(track->GetState( END_ON_PAD )) )
378
flag_erase |= testTrackEndpointDangling( track, ENDPOINT_END );
435
382
// remove segment from board
383
m_Brd->GetRatsnest()->Remove( track );
384
track->ViewRelease();
436
385
track->DeleteStructure();
437
// iterate, because a track connected to the deleted track
438
// is now perhaps now not connected and should be deleted
387
/* keep iterating, because a track connected to the deleted track
388
* now perhaps is not connected and should be deleted */
439
389
item_erased = true;
393
} while( item_erased );
449
// Delete null length segments, and intermediate points ..
450
bool TRACKS_CLEANER::clean_segments()
398
// Delete null length track segments
399
bool TRACKS_CLEANER::delete_null_segments()
452
402
bool modified = false;
453
TRACK* segment, * nextsegment;
458
404
// Delete null segments
459
for( segment = m_Brd->m_Track; segment; segment = nextsegment )
405
for( TRACK *segment = m_Brd->m_Track; segment; segment = nextsegment )
461
407
nextsegment = segment->Next();
463
409
if( segment->IsNull() ) // Length segment = 0; delete it
411
m_Brd->GetRatsnest()->Remove( segment );
412
segment->ViewRelease();
464
413
segment->DeleteStructure();
467
// Delete redundant segments, i.e. segments having the same end points
469
for( segment = m_Brd->m_Track; segment; segment = segment->Next() )
420
bool TRACKS_CLEANER::remove_duplicates_of_track( const TRACK *aTrack )
422
bool modified = false;
425
for( TRACK *other = aTrack->Next(); other; other = nextsegment )
471
for( other = segment->Next(); other; other = nextsegment )
427
nextsegment = other->Next();
429
// New netcode, break out (can't be there any other)
430
if( aTrack->GetNetCode() != other->GetNetCode() )
433
// Must be of the same type, on the same layer and the endpoints
434
// must be the same (maybe swapped)
435
if( (aTrack->Type() != other->Type()) &&
436
(aTrack->GetLayer() != other->GetLayer()) )
473
nextsegment = other->Next();
476
if( segment->Type() != other->Type() )
479
if( segment->GetLayer() != other->GetLayer() )
482
if( segment->GetNetCode() != other->GetNetCode() )
485
if( ( segment->GetStart() == other->GetStart() ) &&
486
( segment->GetEnd() == other->GetEnd() ) )
489
if( ( segment->GetStart() == other->GetEnd() ) &&
490
( segment->GetEnd() == other->GetStart() ) )
493
// Delete redundant point
438
if( ((aTrack->GetStart() == other->GetStart()) &&
439
(aTrack->GetEnd() == other->GetEnd())) ||
440
((aTrack->GetStart() == other->GetEnd()) &&
441
(aTrack->GetEnd() == other->GetStart())))
443
m_Brd->GetRatsnest()->Remove( other );
444
other->ViewRelease();
496
445
other->DeleteStructure();
453
bool TRACKS_CLEANER::merge_collinear_of_track( TRACK *aSegment )
455
bool merged_this = false;
457
// *WHY* doesn't C++ have prec and succ (or ++ --) like PASCAL?
458
for( ENDPOINT_T endpoint = ENDPOINT_START; endpoint <= ENDPOINT_END;
459
endpoint = ENDPOINT_T( endpoint + 1 ) )
461
// search for a possible segment connected to the current endpoint of the current one
462
TRACK *other = aSegment->Next();
465
other = aSegment->GetTrack( other, NULL, endpoint, true, false );
469
// the two segments must have the same width and the other
471
if( (aSegment->GetWidth() == other->GetWidth()) &&
472
(other->Type() == PCB_TRACE_T) )
474
// There can be only one segment connected
475
other->SetState( BUSY, true );
476
TRACK *yet_another = aSegment->GetTrack( m_Brd->m_Track, NULL,
477
endpoint, true, false );
478
other->SetState( BUSY, false );
483
TRACK *segDelete = mergeCollinearSegmentIfPossible( aSegment,
486
// Merge succesful, the other one has to go away
489
m_Brd->GetRatsnest()->Remove( segDelete );
490
segDelete->ViewRelease();
491
segDelete->DeleteStructure();
503
// Delete null length segments, and intermediate points ..
504
bool TRACKS_CLEANER::clean_segments()
506
bool modified = false;
509
modified |= delete_null_segments();
511
// Delete redundant segments, i.e. segments having the same end points and layers
512
for( TRACK *segment = m_Brd->m_Track; segment; segment = segment->Next() )
513
modified |= remove_duplicates_of_track( segment );
502
515
// merge collinear segments:
503
for( segment = m_Brd->m_Track; segment; segment = nextsegment )
517
for( TRACK *segment = m_Brd->m_Track; segment; segment = nextsegment )
509
519
nextsegment = segment->Next();
511
if( segment->Type() != PCB_TRACE_T )
516
// search for a possible point connected to the START point of the current segment
517
for( segStart = segment->Next(); ; )
519
segStart = segment->GetTrace( segStart, NULL, FLG_START );
523
// the two segments must have the same width
524
if( segment->GetWidth() != segStart->GetWidth() )
527
// it cannot be a via
528
if( segStart->Type() != PCB_TRACE_T )
531
// We must have only one segment connected
532
segStart->SetState( BUSY, true );
533
other = segment->GetTrace( m_Brd->m_Track, NULL, FLG_START );
534
segStart->SetState( BUSY, false );
544
if( flag ) // We have the starting point of the segment is connected to an other segment
546
segDelete = mergeCollinearSegmentIfPossible( segment, segStart, FLG_START );
551
segDelete->DeleteStructure();
556
// search for a possible point connected to the END point of the current segment:
557
for( segEnd = segment->Next(); ; )
559
segEnd = segment->GetTrace( segEnd, NULL, FLG_END );
563
if( segment->GetWidth() != segEnd->GetWidth() )
566
if( segEnd->Type() != PCB_TRACE_T )
569
// We must have only one segment connected
570
segEnd->SetState( BUSY, true );
571
other = segment->GetTrace( m_Brd->m_Track, NULL, FLG_END );
572
segEnd->SetState( BUSY, false );
585
if( flag & 2 ) // We have the ending point of the segment is connected to an other segment
587
segDelete = mergeCollinearSegmentIfPossible( segment, segEnd, FLG_END );
592
segDelete->DeleteStructure();
597
if( no_inc ) // The current segment was modified, retry to merge it
598
nextsegment = segment->Next();
521
if( segment->Type() == PCB_TRACE_T )
523
bool merged_this = merge_collinear_of_track( segment );
524
modified |= merged_this;
526
if( merged_this ) // The current segment was modified, retry to merge it again
527
nextsegment = segment->Next();
534
/* Utility: check for parallelism between two segments */
535
static bool parallelism_test( int dx1, int dy1, int dx2, int dy2 )
537
/* The following condition list is ugly and repetitive, but I have
538
* not a better way to express clearly the trivial cases. Hope the
539
* compiler optimize it better than always doing the product
542
// test for vertical alignment (easy to handle)
549
// test for horizontal alignment (easy to handle)
556
/* test for alignment in other cases: Do the usual cross product test
557
* (the same as testing the slope, but without a division) */
558
return ((double)dy1 * dx2 == (double)dx1 * dy2);
605
561
/** Function used by clean_segments.
606
562
* Test if aTrackRef and aCandidate (which must have a common end) are collinear.
615
571
* else return NULL
617
573
TRACK* TRACKS_CLEANER::mergeCollinearSegmentIfPossible( TRACK* aTrackRef, TRACK* aCandidate,
574
ENDPOINT_T aEndType )
620
if( aTrackRef->GetWidth() != aCandidate->GetWidth() )
623
bool is_colinear = false;
625
// Trivial case: superimposed tracks ( tracks, not vias ):
626
if( aTrackRef->Type() == PCB_TRACE_T && aCandidate->Type() == PCB_TRACE_T )
628
if( ( aTrackRef->GetStart() == aCandidate->GetStart() ) &&
629
( aTrackRef->GetEnd() == aCandidate->GetEnd() ) )
632
if( ( aTrackRef->GetStart() == aCandidate->GetEnd() ) &&
633
( aTrackRef->GetEnd() == aCandidate->GetStart() ) )
637
int refdx = aTrackRef->GetEnd().x - aTrackRef->GetStart().x;
638
int refdy = aTrackRef->GetEnd().y - aTrackRef->GetStart().y;
640
int segmdx = aCandidate->GetEnd().x - aCandidate->GetStart().x;
641
int segmdy = aCandidate->GetEnd().y - aCandidate->GetStart().y;
643
// test for vertical alignment (easy to handle)
652
// test for horizontal alignment (easy to handle)
661
/* test if alignment in other cases
662
* We must have refdy/refdx == segmdy/segmdx, (i.e. same slope)
663
* or refdy * segmdx == segmdy * refdx
665
if( is_colinear == false )
667
if( ( double)refdy * segmdx != (double)refdx * segmdy )
576
// First of all, they must be of the same width and must be both actual tracks
577
if( (aTrackRef->GetWidth() != aCandidate->GetWidth()) ||
578
(aTrackRef->Type() != PCB_TRACE_T) ||
579
(aCandidate->Type() != PCB_TRACE_T) )
582
// Trivial case: exactly the same track
583
if( ( aTrackRef->GetStart() == aCandidate->GetStart() ) &&
584
( aTrackRef->GetEnd() == aCandidate->GetEnd() ) )
587
if( ( aTrackRef->GetStart() == aCandidate->GetEnd() ) &&
588
( aTrackRef->GetEnd() == aCandidate->GetStart() ) )
591
// Weed out non-parallel tracks
592
if ( !parallelism_test( aTrackRef->GetEnd().x - aTrackRef->GetStart().x,
593
aTrackRef->GetEnd().y - aTrackRef->GetStart().y,
594
aCandidate->GetEnd().x - aCandidate->GetStart().x,
595
aCandidate->GetEnd().y - aCandidate->GetStart().y ) )
673
598
/* Here we have 2 aligned segments:
674
599
* We must change the pt_ref common point only if not on a pad
675
600
* (this function) is called when there is only 2 connected segments,
676
*and if this point is not on a pad, it can be removed and the 2 segments will be merged
601
* and if this point is not on a pad, it can be removed and the 2 segments will be merged
678
if( aEndType == FLG_START )
603
if( aEndType == ENDPOINT_START )
680
605
// We do not have a pad, which is a always terminal point for a track
681
606
if( aTrackRef->GetState( START_ON_PAD) )