~pierre-parent-k/kicad/length-tunning

« back to all changes in this revision

Viewing changes to pcbnew/clean.cpp

  • Committer: Pierre Parent
  • Date: 2014-07-06 10:32:13 UTC
  • mfrom: (4798.1.179 kicad)
  • Revision ID: pierre.parent@insa-rouen.fr-20140706103213-wjsdy0hc9q6wbz5v
Merge with lp:kicad 4977

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
 
26
26
/**
27
27
 * @file clean.cpp
28
 
 * @brief functions to clean tracks: remove null lenght and redundant segments
 
28
 * @brief functions to clean tracks: remove null length and redundant segments
29
29
 */
30
30
 
31
31
 
38
38
#include <class_track.h>
39
39
#include <connect.h>
40
40
#include <dialog_cleaning_options.h>
 
41
#include <ratsnest_data.h>
41
42
 
42
43
// Helper class used to clean tracks and vias
43
44
class TRACKS_CLEANER: CONNECTIONS
44
45
{
45
46
private:
46
 
    BOARD * m_Brd;
47
 
    bool m_deleteUnconnectedTracks;
48
 
    bool m_mergeSegments;
49
 
    bool m_cleanVias;
 
47
    BOARD *m_Brd;
50
48
 
51
49
public:
52
50
    TRACKS_CLEANER( BOARD * aPcb );
55
53
     * the cleanup function.
56
54
     * return true if some item was modified
57
55
     */
58
 
    bool CleanupBoard();
59
 
 
60
 
    void SetdeleteUnconnectedTracksOpt( bool aDelete )
61
 
    {
62
 
        m_deleteUnconnectedTracks = aDelete;
63
 
    }
64
 
 
65
 
    void SetMergeSegmentsOpt( bool aMerge )
66
 
    {
67
 
        m_mergeSegments = aMerge;
68
 
    }
69
 
 
70
 
    void SetCleanViasOpt( bool aClean )
71
 
    {
72
 
        m_cleanVias = aClean;
73
 
    }
 
56
    bool CleanupBoard( PCB_EDIT_FRAME *aFrame, bool aCleanVias,
 
57
                       bool aMergeSegments, bool aDeleteUnconnected);
74
58
 
75
59
private:
76
60
 
81
65
    bool clean_vias();
82
66
 
83
67
    /**
 
68
     * Removes all the following THT vias on the same position of the
 
69
     * specified one
 
70
     */
 
71
    bool remove_duplicates_of_via( const VIA *aVia );
 
72
 
 
73
    /**
 
74
     * Removes all the following duplicates tracks of the specified one
 
75
     */
 
76
    bool remove_duplicates_of_track( const TRACK *aTrack );
 
77
 
 
78
    /**
84
79
     * Removes dangling tracks
85
80
     */
86
81
    bool deleteUnconnectedTracks();
87
82
 
 
83
    /// Delete null length track segments
 
84
    bool delete_null_segments();
 
85
 
 
86
    /// Try to merge the segment to a following collinear one
 
87
    bool merge_collinear_of_track( TRACK *aSegment );
 
88
 
88
89
    /**
89
 
     * Merge colinear segments and remove null len segments
 
90
     * Merge collinear segments and remove duplicated and null len segments
90
91
     */
91
 
    bool  clean_segments();
 
92
    bool clean_segments();
92
93
 
93
94
    /**
94
95
     * helper function
103
104
     * i.e. when they are colinear, same width, and obviously same layer
104
105
     */
105
106
    TRACK* mergeCollinearSegmentIfPossible( TRACK* aTrackRef,
106
 
                                           TRACK* aCandidate, int aEndType );
 
107
                                           TRACK* aCandidate, ENDPOINT_T aEndType );
 
108
 
 
109
    const ZONE_CONTAINER* zoneForTrackEndpoint( const TRACK *aTrack,
 
110
            ENDPOINT_T aEndPoint );
 
111
 
 
112
    bool testTrackEndpointDangling( TRACK *aTrack, ENDPOINT_T aEndPoint );
107
113
};
108
114
 
109
115
/* Install the cleanup dialog frame to know what should be cleaned
117
123
 
118
124
    wxBusyCursor( dummy );
119
125
    TRACKS_CLEANER cleaner( GetBoard() );
120
 
    cleaner.SetdeleteUnconnectedTracksOpt( dlg.m_deleteUnconnectedSegm );
121
 
    cleaner.SetMergeSegmentsOpt( dlg.m_mergeSegments );
122
 
    cleaner.SetCleanViasOpt( dlg.m_cleanVias );
123
 
 
124
 
    if( cleaner.CleanupBoard() )
125
 
    {
126
 
        // Clear undo and redo lists to avoid inconsistencies between lists
127
 
        GetScreen()->ClearUndoRedoList();
128
 
        SetCurItem( NULL );
129
 
        Compile_Ratsnest( NULL, true );
130
 
        OnModify();
131
 
    }
132
 
 
 
126
 
 
127
    cleaner.CleanupBoard( this, dlg.m_cleanVias, dlg.m_mergeSegments,
 
128
                          dlg.m_deleteUnconnectedSegm );
133
129
    m_canvas->Refresh( true );
134
130
}
135
131
 
138
134
 *  Delete
139
135
 * - Redundant points on tracks (merge aligned segments)
140
136
 * - vias on pad
141
 
 * - null lenght segments
 
137
 * - null length segments
142
138
 *  Create segments when track ends are incorrectly connected:
143
139
 *  i.e. when a track end covers a pad or a via but is not exactly on the pad or the via center
144
140
 */
145
 
bool TRACKS_CLEANER::CleanupBoard()
 
141
bool TRACKS_CLEANER::CleanupBoard( PCB_EDIT_FRAME *aFrame,
 
142
                                   bool aCleanVias,
 
143
                                   bool aMergeSegments,
 
144
                                   bool aDeleteUnconnected )
146
145
{
147
146
    bool modified = false;
148
147
 
149
148
    // delete redundant vias
150
 
    if( m_cleanVias && clean_vias() )
151
 
        modified = true;
 
149
    modified |= (aCleanVias && clean_vias());
152
150
 
153
151
    // Remove null segments and intermediate points on aligned segments
154
 
    if( m_mergeSegments && clean_segments() )
155
 
        modified = true;
 
152
    modified |= (aMergeSegments && clean_segments());
156
153
 
157
154
    // Delete dangling tracks
158
 
    if( m_deleteUnconnectedTracks && deleteUnconnectedTracks() )
159
 
        modified = true;
 
155
    modified |= (aDeleteUnconnected && deleteUnconnectedTracks());
160
156
 
 
157
    if( modified )
 
158
    {
 
159
        // Clear undo and redo lists to avoid inconsistencies between lists
 
160
        aFrame->GetScreen()->ClearUndoRedoList();
 
161
        aFrame->SetCurItem( NULL );
 
162
        aFrame->Compile_Ratsnest( NULL, true );
 
163
        aFrame->OnModify();
 
164
    }
161
165
    return modified;
162
166
}
163
167
 
164
168
TRACKS_CLEANER::TRACKS_CLEANER( BOARD * aPcb ): CONNECTIONS( aPcb )
165
169
{
166
170
    m_Brd = aPcb;
167
 
    m_deleteUnconnectedTracks = false;
168
 
    m_mergeSegments = false;
169
171
 
170
172
    // Build connections info
171
173
    BuildPadsList();
177
179
    BuildTracksCandidatesList( m_Brd->m_Track, NULL);
178
180
 
179
181
    // clear flags and variables used in cleanup
180
 
    for( TRACK * track = m_Brd->m_Track; track; track = track->Next() )
 
182
    for( TRACK *track = m_Brd->m_Track; track != NULL; track = track->Next() )
181
183
    {
182
184
        track->start = NULL;
183
185
        track->end = NULL;
187
189
 
188
190
    // Build connections info tracks to pads
189
191
    SearchTracksConnectedToPads();
190
 
    for( TRACK * track = m_Brd->m_Track; track; track = track->Next() )
 
192
    for( TRACK *track = m_Brd->m_Track; track != NULL; track = track->Next() )
191
193
    {
192
194
        // Mark track if connected to pads
193
195
        for( unsigned jj = 0; jj < track->m_PadsConnected.size(); jj++ )
209
211
    }
210
212
}
211
213
 
 
214
bool TRACKS_CLEANER::remove_duplicates_of_via( const VIA *aVia )
 
215
{
 
216
    bool modified = false;
 
217
 
 
218
    // Search and delete others vias at same location
 
219
    VIA* next_via;
 
220
    for( VIA* alt_via = GetFirstVia( aVia->Next() ); alt_via != NULL;
 
221
            alt_via = next_via )
 
222
    {
 
223
        next_via = GetFirstVia( alt_via->Next() );
 
224
 
 
225
        if( (alt_via->GetViaType() == VIA_THROUGH) &&
 
226
                (alt_via->GetStart() == aVia->GetStart()) )
 
227
        {
 
228
            // delete via
 
229
            m_Brd->GetRatsnest()->Remove( alt_via );
 
230
            alt_via->ViewRelease();
 
231
            alt_via->DeleteStructure();
 
232
            modified = true;
 
233
        }
 
234
    }
 
235
    return modified;
 
236
}
 
237
 
212
238
bool TRACKS_CLEANER::clean_vias()
213
239
{
214
 
    TRACK* next_track;
215
240
    bool modified = false;
216
241
 
217
 
    for( TRACK* track = m_Brd->m_Track; track; track = track->Next() )
218
 
    {
219
 
        // Correct via m_End defects (if any)
220
 
        if( track->Type() == PCB_VIA_T )
221
 
        {
222
 
            if( track->GetStart() != track->GetEnd() )
223
 
                track->SetEnd( track->GetStart() );
224
 
        }
225
 
 
226
 
        if( track->GetShape() != VIA_THROUGH )
227
 
            continue;
228
 
 
229
 
        // Search and delete others vias at same location
230
 
        TRACK* alt_track = track->Next();
231
 
 
232
 
        for( ; alt_track != NULL; alt_track = next_track )
233
 
        {
234
 
            next_track = alt_track->Next();
235
 
 
236
 
            if( alt_track->GetShape() != VIA_THROUGH )
237
 
                continue;
238
 
 
239
 
            if( alt_track->GetStart() != track->GetStart() )
240
 
                continue;
241
 
 
242
 
            // delete via
243
 
            alt_track->UnLink();
244
 
            delete alt_track;
245
 
            modified = true;
246
 
        }
247
 
    }
248
 
 
249
 
    // Delete Via on pads at same location
250
 
    for( TRACK* track = m_Brd->m_Track; track != NULL; track = next_track )
251
 
    {
252
 
        next_track = track->Next();
253
 
 
254
 
        if( track->GetShape() != VIA_THROUGH )
255
 
            continue;
256
 
 
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++ )
260
 
        {
261
 
            D_PAD * pad = track->m_PadsConnected[ii];
262
 
 
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() ) )
 
244
    {
 
245
        // Correct via m_End defects (if any), should never happen
 
246
        if( via->GetStart() != via->GetEnd() )
 
247
        {
 
248
            wxFAIL_MSG( wxT( "Via with mismatching ends" ) );
 
249
            via->SetEnd( via->GetStart() );
 
250
        }
 
251
 
 
252
        /* Important: these cleanups only do thru hole vias, they don't
 
253
         * (yet) handle high density interconnects */
 
254
        if( via->GetViaType() != VIA_THROUGH )
 
255
        {
 
256
            modified |= remove_duplicates_of_via( via );
 
257
 
 
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 )
264
262
            {
265
 
                // redundant: via delete it
266
 
                track->UnLink();
267
 
                delete track;
268
 
                modified = true;
269
 
                break;
 
263
                const D_PAD *pad = via->m_PadsConnected[ii];
 
264
 
 
265
                const LSET all_cu = LSET::AllCuMask();
 
266
 
 
267
                if( (pad->GetLayerSet() & all_cu) == all_cu )
 
268
                {
 
269
                    // redundant: delete the via
 
270
                    m_Brd->GetRatsnest()->Remove( via );
 
271
                    via->ViewRelease();
 
272
                    via->DeleteStructure();
 
273
                    modified = true;
 
274
                    break;
 
275
                }
270
276
            }
271
277
        }
272
278
    }
274
280
    return modified;
275
281
}
276
282
 
 
283
/// Utility for checking if a track/via ends on a zone
 
284
const ZONE_CONTAINER* TRACKS_CLEANER::zoneForTrackEndpoint( const TRACK *aTrack,
 
285
        ENDPOINT_T aEndPoint )
 
286
{
 
287
    // Vias are special cased, since they get a layer range, not a single one
 
288
    LAYER_ID    top_layer, bottom_layer;
 
289
    const VIA*  via = dyn_cast<const VIA*>( aTrack );
 
290
 
 
291
    if( via )
 
292
        via->LayerPair( &top_layer, &bottom_layer );
 
293
    else
 
294
    {
 
295
        top_layer = aTrack->GetLayer();
 
296
        bottom_layer = top_layer;
 
297
    }
 
298
    return m_Brd->HitTestForAnyFilledArea( aTrack->GetEndPoint( aEndPoint ),
 
299
            top_layer, bottom_layer, aTrack->GetNetCode() );
 
300
}
 
301
 
 
302
/** Utility: does the endpoint unconnected processed for one endpoint of one track
 
303
 * Returns true if the track must be deleted, false if not necessarily */
 
304
bool TRACKS_CLEANER::testTrackEndpointDangling( TRACK *aTrack, ENDPOINT_T aEndPoint )
 
305
{
 
306
    bool flag_erase = false;
 
307
 
 
308
    TRACK* other = aTrack->GetTrack( m_Brd->m_Track, NULL, aEndPoint, true, false );
 
309
    if( (other == NULL) &&
 
310
            (zoneForTrackEndpoint( aTrack, aEndPoint ) == NULL) )
 
311
        flag_erase = true; // Start endpoint is neither on pad, zone or other track
 
312
    else    // segment, via or zone connected to this end
 
313
    {
 
314
        // Fill connectivity informations
 
315
        if( aEndPoint == ENDPOINT_START )
 
316
            aTrack->start = other;
 
317
        else
 
318
            aTrack->end = other;
 
319
 
 
320
        /* If a via is connected to this end, test if this via has a second item connected.
 
321
         * If not, remove the current segment (the via would then become
 
322
         * unconnected and remove on the following pass) */
 
323
        VIA* via = dyn_cast<VIA*>( other );
 
324
 
 
325
        if( via )
 
326
        {
 
327
            // search for another segment following the via
 
328
            aTrack->SetState( BUSY, true );
 
329
 
 
330
            other = via->GetTrack( m_Brd->m_Track, NULL, aEndPoint, true, false );
 
331
 
 
332
            // There is a via on the start but it goes nowhere
 
333
            if( (other == NULL) &&
 
334
                    (zoneForTrackEndpoint( via, aEndPoint ) == NULL) )
 
335
                flag_erase = true;
 
336
 
 
337
            aTrack->SetState( BUSY, false );
 
338
        }
 
339
    }
 
340
    return flag_erase;
 
341
}
277
342
 
278
343
/*
279
344
 *  Delete dangling tracks
286
351
        return false;
287
352
 
288
353
    bool modified = false;
289
 
    bool item_erased = true;
290
 
    while( item_erased )    // Iterate when at least one track is deleted
 
354
    bool item_erased;
 
355
    do // Iterate when at least one track is deleted
291
356
    {
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 )
295
360
        {
296
361
            next_track = track->Next();
297
362
 
298
 
            int flag_erase = 0; //Not connected indicator
299
 
            int type_end = 0;
300
 
 
301
 
            if( track->GetState( START_ON_PAD ) )
302
 
                type_end |= START_ON_PAD;
303
 
 
304
 
            if( track->GetState( END_ON_PAD ) )
305
 
                type_end |= END_ON_PAD;
306
 
 
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;
314
 
 
315
 
            if( (type_end & START_ON_PAD ) == 0 )
316
 
            {
317
 
                TRACK* other = track->GetTrace( m_Brd->m_Track, NULL, FLG_START );
318
 
 
319
 
                if( other == NULL )     // Test a connection to zones
320
 
                {
321
 
                    if( track->Type() != PCB_VIA_T )
322
 
                    {
323
 
                        zone = m_Brd->HitTestForAnyFilledArea( track->GetStart(),
324
 
                                                               track->GetLayer(),
325
 
                                                               track->GetLayer(),
326
 
                                                               track->GetNetCode() );
327
 
                    }
328
 
                    else
329
 
                    {
330
 
                        ((SEGVIA*)track)->LayerPair( &top_layer, &bottom_layer );
331
 
                        zone = m_Brd->HitTestForAnyFilledArea( track->GetStart(),
332
 
                                                               top_layer, bottom_layer,
333
 
                                                               track->GetNetCode() );
334
 
                    }
335
 
                }
336
 
 
337
 
                if( (other == NULL) && (zone == NULL) )
338
 
                {
339
 
                    flag_erase |= 1;
340
 
                }
341
 
                else    // segment, via or zone connected to this end
342
 
                {
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
347
 
 
348
 
                    if( other && other->Type() == PCB_VIA_T )
349
 
                    {
350
 
                        // search for another segment following the via
351
 
                        track->SetState( BUSY, true );
352
 
 
353
 
                        SEGVIA* via = (SEGVIA*) other;
354
 
                        other = via->GetTrace( m_Brd->m_Track, NULL, FLG_START );
355
 
 
356
 
                        if( other == NULL )
357
 
                        {
358
 
                            via->LayerPair( &top_layer, &bottom_layer );
359
 
                            zone = m_Brd->HitTestForAnyFilledArea( via->GetStart(),
360
 
                                                                   bottom_layer,
361
 
                                                                   top_layer,
362
 
                                                                   via->GetNetCode() );
363
 
                        }
364
 
 
365
 
                        if( (other == NULL) && (zone == NULL) )
366
 
                            flag_erase |= 2;
367
 
 
368
 
                        track->SetState( BUSY, false );
369
 
                    }
370
 
                }
371
 
            }
372
 
 
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 )
376
 
            {
377
 
                TRACK* other = track->GetTrace( m_Brd->m_Track, NULL, FLG_END );
378
 
 
379
 
                if( other == NULL )     // Test a connection to zones
380
 
                {
381
 
                    if( track->Type() != PCB_VIA_T )
382
 
                    {
383
 
                        zone = m_Brd->HitTestForAnyFilledArea( track->GetEnd(),
384
 
                                                               track->GetLayer(),
385
 
                                                               track->GetLayer(),
386
 
                                                               track->GetNetCode() );
387
 
                    }
388
 
                    else
389
 
                    {
390
 
                        ((SEGVIA*)track)->LayerPair( &top_layer, &bottom_layer );
391
 
                        zone = m_Brd->HitTestForAnyFilledArea( track->GetEnd(),
392
 
                                                               top_layer, bottom_layer,
393
 
                                                               track->GetNetCode() );
394
 
                    }
395
 
                }
396
 
 
397
 
                if ( (other == NULL) && (zone == NULL) )
398
 
                {
399
 
                    flag_erase |= 0x10;
400
 
                }
401
 
                else     // segment, via or zone connected to this end
402
 
                {
403
 
                    track->end = other;
404
 
 
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
407
 
 
408
 
                    if( other && other->Type() == PCB_VIA_T )
409
 
                    {
410
 
                        // search for another segment following the via
411
 
 
412
 
                        track->SetState( BUSY, true );
413
 
 
414
 
                        SEGVIA* via = (SEGVIA*) other;
415
 
                        other = via->GetTrace( m_Brd->m_Track, NULL, FLG_END );
416
 
 
417
 
                        if( other == NULL )
418
 
                        {
419
 
                            via->LayerPair( &top_layer, &bottom_layer );
420
 
                            zone = m_Brd->HitTestForAnyFilledArea( via->GetEnd(),
421
 
                                                                   bottom_layer, top_layer,
422
 
                                                                   via->GetNetCode() );
423
 
                        }
424
 
 
425
 
                        if( (other == NULL) && (zone == NULL) )
426
 
                            flag_erase |= 0x20;
427
 
 
428
 
                        track->SetState( BUSY, false );
429
 
                    }
430
 
                }
431
 
            }
 
363
            bool flag_erase = false; // Start without a good reason to erase it
 
364
 
 
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
 
370
             * same layer */
 
371
 
 
372
            // Check if there is nothing attached on the start
 
373
            if( !(track->GetState( START_ON_PAD )) )
 
374
                flag_erase |= testTrackEndpointDangling( track, ENDPOINT_START );
 
375
 
 
376
            // Check if there is nothing attached on the end
 
377
            if( !(track->GetState( END_ON_PAD )) )
 
378
                flag_erase |= testTrackEndpointDangling( track, ENDPOINT_END );
432
379
 
433
380
            if( flag_erase )
434
381
            {
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
 
386
 
 
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;
440
390
                modified = true;
441
391
            }
442
392
        }
443
 
    }
 
393
    } while( item_erased );
444
394
 
445
395
    return modified;
446
396
}
447
397
 
448
 
 
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()
451
400
{
 
401
    TRACK *nextsegment;
452
402
    bool modified = false;
453
 
    TRACK*          segment, * nextsegment;
454
 
    TRACK*          other;
455
 
    int             flag, no_inc;
456
 
 
457
403
 
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 )
460
406
    {
461
407
        nextsegment = segment->Next();
462
408
 
463
409
        if( segment->IsNull() )     // Length segment = 0; delete it
 
410
        {
 
411
            m_Brd->GetRatsnest()->Remove( segment );
 
412
            segment->ViewRelease();
464
413
            segment->DeleteStructure();
 
414
            modified = true;
 
415
        }
465
416
    }
466
 
 
467
 
    // Delete redundant segments, i.e. segments having the same end points
468
 
    // and layers
469
 
    for( segment  = m_Brd->m_Track; segment; segment = segment->Next() )
 
417
    return modified;
 
418
}
 
419
 
 
420
bool TRACKS_CLEANER::remove_duplicates_of_track( const TRACK *aTrack )
 
421
{
 
422
    bool modified = false;
 
423
 
 
424
    TRACK *nextsegment;
 
425
    for( TRACK *other = aTrack->Next(); other; other = nextsegment )
470
426
    {
471
 
        for( other = segment->Next(); other; other = nextsegment )
 
427
        nextsegment = other->Next();
 
428
 
 
429
        // New netcode, break out (can't be there any other)
 
430
        if( aTrack->GetNetCode() != other->GetNetCode() )
 
431
            break;
 
432
 
 
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()) )
472
437
        {
473
 
            nextsegment = other->Next();
474
 
            bool erase = false;
475
 
 
476
 
            if( segment->Type() != other->Type() )
477
 
                continue;
478
 
 
479
 
            if( segment->GetLayer() != other->GetLayer() )
480
 
                continue;
481
 
 
482
 
            if( segment->GetNetCode() != other->GetNetCode() )
483
 
                break;
484
 
 
485
 
            if( ( segment->GetStart() == other->GetStart() ) &&
486
 
                ( segment->GetEnd() == other->GetEnd() ) )
487
 
                erase = true;
488
 
 
489
 
            if( ( segment->GetStart() == other->GetEnd() ) &&
490
 
                ( segment->GetEnd() == other->GetStart() ) )
491
 
                erase = true;
492
 
 
493
 
            // Delete redundant point
494
 
            if( erase )
 
438
            if( ((aTrack->GetStart() == other->GetStart()) &&
 
439
                 (aTrack->GetEnd() == other->GetEnd())) ||
 
440
                ((aTrack->GetStart() == other->GetEnd()) &&
 
441
                 (aTrack->GetEnd() == other->GetStart())))
495
442
            {
 
443
                m_Brd->GetRatsnest()->Remove( other );
 
444
                other->ViewRelease();
496
445
                other->DeleteStructure();
497
446
                modified = true;
498
447
            }
499
448
        }
500
449
    }
 
450
    return modified;
 
451
}
 
452
 
 
453
bool TRACKS_CLEANER::merge_collinear_of_track( TRACK *aSegment )
 
454
{
 
455
    bool merged_this = false;
 
456
 
 
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 ) )
 
460
    {
 
461
        // search for a possible segment connected to the current endpoint of the current one
 
462
        TRACK *other = aSegment->Next();
 
463
        if( other )
 
464
        {
 
465
            other = aSegment->GetTrack( other, NULL, endpoint, true, false );
 
466
 
 
467
            if( other )
 
468
            {
 
469
                // the two segments must have the same width and the other
 
470
                // cannot be a via
 
471
                if( (aSegment->GetWidth() == other->GetWidth()) &&
 
472
                        (other->Type() == PCB_TRACE_T) )
 
473
                {
 
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 );
 
479
 
 
480
                    if( !yet_another )
 
481
                    {
 
482
                        // Try to merge them
 
483
                        TRACK *segDelete = mergeCollinearSegmentIfPossible( aSegment,
 
484
                                other, endpoint );
 
485
 
 
486
                        // Merge succesful, the other one has to go away
 
487
                        if( segDelete )
 
488
                        {
 
489
                            m_Brd->GetRatsnest()->Remove( segDelete );
 
490
                            segDelete->ViewRelease();
 
491
                            segDelete->DeleteStructure();
 
492
                            merged_this = true;
 
493
                        }
 
494
                    }
 
495
                }
 
496
            }
 
497
        }
 
498
    }
 
499
 
 
500
    return merged_this;
 
501
}
 
502
 
 
503
// Delete null length segments, and intermediate points ..
 
504
bool TRACKS_CLEANER::clean_segments()
 
505
{
 
506
    bool modified = false;
 
507
 
 
508
    // Easy things first
 
509
    modified |= delete_null_segments();
 
510
 
 
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 );
501
514
 
502
515
    // merge collinear segments:
503
 
    for( segment = m_Brd->m_Track; segment; segment = nextsegment )
 
516
    TRACK *nextsegment;
 
517
    for( TRACK *segment = m_Brd->m_Track; segment; segment = nextsegment )
504
518
    {
505
 
        TRACK*  segStart;
506
 
        TRACK*  segEnd;
507
 
        TRACK*  segDelete;
508
 
 
509
519
        nextsegment = segment->Next();
510
520
 
511
 
        if( segment->Type() != PCB_TRACE_T )
512
 
            continue;
513
 
 
514
 
        flag = no_inc = 0;
515
 
 
516
 
        // search for a possible point connected to the START point of the current segment
517
 
        for( segStart = segment->Next(); ; )
518
 
        {
519
 
            segStart = segment->GetTrace( segStart, NULL, FLG_START );
520
 
 
521
 
            if( segStart )
522
 
            {
523
 
                // the two segments must have the same width
524
 
                if( segment->GetWidth() != segStart->GetWidth() )
525
 
                    break;
526
 
 
527
 
                // it cannot be a via
528
 
                if( segStart->Type() != PCB_TRACE_T )
529
 
                    break;
530
 
 
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 );
535
 
 
536
 
                if( other == NULL )
537
 
                    flag = 1;           // OK
538
 
 
539
 
                break;
540
 
            }
541
 
            break;
542
 
        }
543
 
 
544
 
        if( flag )   // We have the starting point of the segment is connected to an other segment
545
 
        {
546
 
            segDelete = mergeCollinearSegmentIfPossible( segment, segStart, FLG_START );
547
 
 
548
 
            if( segDelete )
549
 
            {
550
 
                no_inc = 1;
551
 
                segDelete->DeleteStructure();
552
 
                modified = true;
553
 
            }
554
 
        }
555
 
 
556
 
        // search for a possible point connected to the END point of the current segment:
557
 
        for( segEnd = segment->Next(); ; )
558
 
        {
559
 
            segEnd = segment->GetTrace( segEnd, NULL, FLG_END );
560
 
 
561
 
            if( segEnd )
562
 
            {
563
 
                if( segment->GetWidth() != segEnd->GetWidth() )
564
 
                    break;
565
 
 
566
 
                if( segEnd->Type() != PCB_TRACE_T )
567
 
                    break;
568
 
 
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 );
573
 
 
574
 
                if( other == NULL )
575
 
                    flag |= 2;          // Ok
576
 
 
577
 
                break;
578
 
            }
579
 
            else
580
 
            {
581
 
                break;
582
 
            }
583
 
        }
584
 
 
585
 
        if( flag & 2 )  // We have the ending point of the segment is connected to an other segment
586
 
        {
587
 
            segDelete = mergeCollinearSegmentIfPossible( segment, segEnd, FLG_END );
588
 
 
589
 
            if( segDelete )
590
 
            {
591
 
                no_inc = 1;
592
 
                segDelete->DeleteStructure();
593
 
                modified = true;
594
 
            }
595
 
        }
596
 
 
597
 
        if( no_inc ) // The current segment was modified, retry to merge it
598
 
            nextsegment = segment->Next();
 
521
        if( segment->Type() == PCB_TRACE_T )
 
522
        {
 
523
            bool merged_this = merge_collinear_of_track( segment );
 
524
            modified |= merged_this;
 
525
 
 
526
            if( merged_this ) // The current segment was modified, retry to merge it again
 
527
                nextsegment = segment->Next();
 
528
        }
599
529
    }
600
530
 
601
531
    return modified;
602
532
}
603
533
 
 
534
/* Utility: check for parallelism between two segments */
 
535
static bool parallelism_test( int dx1, int dy1, int dx2, int dy2 )
 
536
{
 
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
 
540
     * below... */
 
541
 
 
542
    // test for vertical alignment (easy to handle)
 
543
    if( dx1 == 0 )
 
544
        return dx2 == 0;
 
545
 
 
546
    if( dx2 == 0 )
 
547
        return dx1 == 0;
 
548
 
 
549
    // test for horizontal alignment (easy to handle)
 
550
    if( dy1 == 0 )
 
551
        return dy2 == 0;
 
552
 
 
553
    if( dy2 == 0 )
 
554
        return dy1 == 0;
 
555
 
 
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);
 
559
}
604
560
 
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
616
572
 */
617
573
TRACK* TRACKS_CLEANER::mergeCollinearSegmentIfPossible( TRACK* aTrackRef, TRACK* aCandidate,
618
 
                                       int aEndType )
 
574
                                       ENDPOINT_T aEndType )
619
575
{
620
 
    if( aTrackRef->GetWidth() != aCandidate->GetWidth() )
621
 
        return NULL;
622
 
 
623
 
    bool is_colinear = false;
624
 
 
625
 
    // Trivial case: superimposed tracks ( tracks, not vias ):
626
 
    if( aTrackRef->Type() == PCB_TRACE_T && aCandidate->Type() == PCB_TRACE_T )
627
 
    {
628
 
        if( ( aTrackRef->GetStart() == aCandidate->GetStart() ) &&
629
 
            ( aTrackRef->GetEnd() == aCandidate->GetEnd() ) )
630
 
            return aCandidate;
631
 
 
632
 
        if( ( aTrackRef->GetStart() == aCandidate->GetEnd() ) &&
633
 
            ( aTrackRef->GetEnd() == aCandidate->GetStart() ) )
634
 
            return aCandidate;
635
 
    }
636
 
 
637
 
    int refdx = aTrackRef->GetEnd().x - aTrackRef->GetStart().x;
638
 
    int refdy = aTrackRef->GetEnd().y - aTrackRef->GetStart().y;
639
 
 
640
 
    int segmdx = aCandidate->GetEnd().x - aCandidate->GetStart().x;
641
 
    int segmdy = aCandidate->GetEnd().y - aCandidate->GetStart().y;
642
 
 
643
 
    // test for vertical alignment (easy to handle)
644
 
    if( refdx == 0 )
645
 
    {
646
 
        if( segmdx != 0 )
647
 
            return NULL;
648
 
        else
649
 
            is_colinear = true;
650
 
    }
651
 
 
652
 
    // test for horizontal alignment (easy to handle)
653
 
    if( refdy == 0 )
654
 
    {
655
 
        if( segmdy != 0 )
656
 
            return NULL;
657
 
        else
658
 
            is_colinear = true;
659
 
    }
660
 
 
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
664
 
     */
665
 
    if( is_colinear == false )
666
 
    {
667
 
        if( ( double)refdy * segmdx != (double)refdx * segmdy )
668
 
            return NULL;
669
 
 
670
 
        is_colinear = true;
671
 
    }
 
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) )
 
580
        return NULL;
 
581
 
 
582
    // Trivial case: exactly the same track
 
583
    if( ( aTrackRef->GetStart() == aCandidate->GetStart() ) &&
 
584
        ( aTrackRef->GetEnd() == aCandidate->GetEnd() ) )
 
585
        return aCandidate;
 
586
 
 
587
    if( ( aTrackRef->GetStart() == aCandidate->GetEnd() ) &&
 
588
        ( aTrackRef->GetEnd() == aCandidate->GetStart() ) )
 
589
        return aCandidate;
 
590
 
 
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 ) )
 
596
        return NULL;
672
597
 
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
677
602
     */
678
 
    if( aEndType == FLG_START )
 
603
    if( aEndType == ENDPOINT_START )
679
604
    {
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) )
688
613
            aTrackRef->SetStart( aCandidate->GetEnd());
689
614
            aTrackRef->start = aCandidate->end;
690
615
            aTrackRef->SetState( START_ON_PAD, aCandidate->GetState( END_ON_PAD) );
 
616
            aTrackRef->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
691
617
            return aCandidate;
692
618
        }
693
619
        else
695
621
            aTrackRef->SetStart( aCandidate->GetStart() );
696
622
            aTrackRef->start = aCandidate->start;
697
623
            aTrackRef->SetState( START_ON_PAD, aCandidate->GetState( START_ON_PAD) );
 
624
            aTrackRef->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
698
625
            return aCandidate;
699
626
        }
700
627
    }
711
638
            aTrackRef->SetEnd( aCandidate->GetEnd() );
712
639
            aTrackRef->end = aCandidate->end;
713
640
            aTrackRef->SetState( END_ON_PAD, aCandidate->GetState( END_ON_PAD) );
 
641
            aTrackRef->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
714
642
            return aCandidate;
715
643
        }
716
644
        else
718
646
            aTrackRef->SetEnd( aCandidate->GetStart() );
719
647
            aTrackRef->end = aCandidate->start;
720
648
            aTrackRef->SetState( END_ON_PAD, aCandidate->GetState( START_ON_PAD) );
 
649
            aTrackRef->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
721
650
            return aCandidate;
722
651
        }
723
652
    }
752
681
        }
753
682
        else
754
683
        {
755
 
            other = segment->GetTrace( GetBoard()->m_Track, NULL, FLG_START );
 
684
            other = segment->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_START, false, false );
756
685
 
757
686
            if( other )
758
687
                net_code_s = other->GetNetCode();
770
699
        }
771
700
        else
772
701
        {
773
 
            other = segment->GetTrace( GetBoard()->m_Track, NULL, FLG_END );
 
702
            other = segment->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_END, false, false );
774
703
 
775
704
            if( other )
776
705
                net_code_e = other->GetNetCode();