You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

737 lines
24 KiB

12 years ago
12 years ago
14 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2004-2010 Jean-Pierre Charras, jean-pierre.charras@gpisa-lab.inpg.fr
  5. * Copyright (C) 2011 Wayne Stambaugh <stambaughw@verizon.net>
  6. * Copyright (C) 1992-2011 KiCad Developers, see change_log.txt for contributors.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. /**
  26. * @file clean.cpp
  27. * @brief functions to clean tracks: remove null length and redundant segments
  28. */
  29. #include <fctsys.h>
  30. #include <class_drawpanel.h>
  31. #include <wxPcbStruct.h>
  32. #include <pcbnew.h>
  33. #include <class_board.h>
  34. #include <class_track.h>
  35. #include <connect.h>
  36. #include <dialog_cleaning_options.h>
  37. #include <ratsnest_data.h>
  38. // Helper class used to clean tracks and vias
  39. class TRACKS_CLEANER: CONNECTIONS
  40. {
  41. private:
  42. BOARD *m_Brd;
  43. public:
  44. TRACKS_CLEANER( BOARD * aPcb );
  45. /**
  46. * the cleanup function.
  47. * return true if some item was modified
  48. */
  49. bool CleanupBoard( PCB_EDIT_FRAME *aFrame, bool aCleanVias,
  50. bool aMergeSegments, bool aDeleteUnconnected);
  51. private:
  52. /**
  53. * Removes redundant vias like vias at same location
  54. * or on pad through
  55. */
  56. bool clean_vias();
  57. /**
  58. * Removes all the following THT vias on the same position of the
  59. * specified one
  60. */
  61. bool remove_duplicates_of_via( const VIA *aVia );
  62. /**
  63. * Removes all the following duplicates tracks of the specified one
  64. */
  65. bool remove_duplicates_of_track( const TRACK *aTrack );
  66. /**
  67. * Removes dangling tracks
  68. */
  69. bool deleteUnconnectedTracks();
  70. /// Delete null length track segments
  71. bool delete_null_segments();
  72. /// Try to merge the segment to a following collinear one
  73. bool merge_collinear_of_track( TRACK *aSegment );
  74. /**
  75. * Merge collinear segments and remove duplicated and null len segments
  76. */
  77. bool clean_segments();
  78. /**
  79. * helper function
  80. * Rebuild list of tracks, and connected tracks
  81. * this info must be rebuilt when tracks are erased
  82. */
  83. void buildTrackConnectionInfo();
  84. /**
  85. * helper function
  86. * merge aTrackRef and aCandidate, when possible,
  87. * i.e. when they are colinear, same width, and obviously same layer
  88. */
  89. TRACK* mergeCollinearSegmentIfPossible( TRACK* aTrackRef,
  90. TRACK* aCandidate, ENDPOINT_T aEndType );
  91. const ZONE_CONTAINER* zoneForTrackEndpoint( const TRACK *aTrack,
  92. ENDPOINT_T aEndPoint );
  93. bool testTrackEndpointDangling( TRACK *aTrack, ENDPOINT_T aEndPoint );
  94. };
  95. /* Install the cleanup dialog frame to know what should be cleaned
  96. */
  97. void PCB_EDIT_FRAME::Clean_Pcb()
  98. {
  99. DIALOG_CLEANING_OPTIONS dlg( this );
  100. if( dlg.ShowModal() != wxID_OK )
  101. return;
  102. wxBusyCursor( dummy );
  103. TRACKS_CLEANER cleaner( GetBoard() );
  104. cleaner.CleanupBoard( this, dlg.m_cleanVias, dlg.m_mergeSegments,
  105. dlg.m_deleteUnconnectedSegm );
  106. m_canvas->Refresh( true );
  107. }
  108. /* Main cleaning function.
  109. * Delete
  110. * - Redundant points on tracks (merge aligned segments)
  111. * - vias on pad
  112. * - null length segments
  113. * Create segments when track ends are incorrectly connected:
  114. * i.e. when a track end covers a pad or a via but is not exactly on the pad or the via center
  115. */
  116. bool TRACKS_CLEANER::CleanupBoard( PCB_EDIT_FRAME *aFrame,
  117. bool aCleanVias,
  118. bool aMergeSegments,
  119. bool aDeleteUnconnected )
  120. {
  121. bool modified = false;
  122. // delete redundant vias
  123. modified |= (aCleanVias && clean_vias());
  124. // Remove null segments and intermediate points on aligned segments
  125. modified |= (aMergeSegments && clean_segments());
  126. // Delete dangling tracks
  127. modified |= (aDeleteUnconnected && deleteUnconnectedTracks());
  128. if( modified )
  129. {
  130. // Clear undo and redo lists to avoid inconsistencies between lists
  131. aFrame->GetScreen()->ClearUndoRedoList();
  132. aFrame->SetCurItem( NULL );
  133. aFrame->Compile_Ratsnest( NULL, true );
  134. aFrame->OnModify();
  135. }
  136. return modified;
  137. }
  138. TRACKS_CLEANER::TRACKS_CLEANER( BOARD * aPcb ): CONNECTIONS( aPcb )
  139. {
  140. m_Brd = aPcb;
  141. // Build connections info
  142. BuildPadsList();
  143. buildTrackConnectionInfo();
  144. }
  145. void TRACKS_CLEANER::buildTrackConnectionInfo()
  146. {
  147. BuildTracksCandidatesList( m_Brd->m_Track, NULL);
  148. // clear flags and variables used in cleanup
  149. for( TRACK *track = m_Brd->m_Track; track != NULL; track = track->Next() )
  150. {
  151. track->start = NULL;
  152. track->end = NULL;
  153. track->m_PadsConnected.clear();
  154. track->SetState( START_ON_PAD|END_ON_PAD|BUSY, false );
  155. }
  156. // Build connections info tracks to pads
  157. SearchTracksConnectedToPads();
  158. for( TRACK *track = m_Brd->m_Track; track != NULL; track = track->Next() )
  159. {
  160. // Mark track if connected to pads
  161. for( unsigned jj = 0; jj < track->m_PadsConnected.size(); jj++ )
  162. {
  163. D_PAD * pad = track->m_PadsConnected[jj];
  164. if( pad->HitTest( track->GetStart() ) )
  165. {
  166. track->start = pad;
  167. track->SetState( START_ON_PAD, true );
  168. }
  169. if( pad->HitTest( track->GetEnd() ) )
  170. {
  171. track->end = pad;
  172. track->SetState( END_ON_PAD, true );
  173. }
  174. }
  175. }
  176. }
  177. bool TRACKS_CLEANER::remove_duplicates_of_via( const VIA *aVia )
  178. {
  179. bool modified = false;
  180. // Search and delete others vias at same location
  181. VIA* next_via;
  182. for( VIA* alt_via = GetFirstVia( aVia->Next() ); alt_via != NULL;
  183. alt_via = next_via )
  184. {
  185. next_via = GetFirstVia( alt_via->Next() );
  186. if( (alt_via->GetViaType() == VIA_THROUGH) &&
  187. (alt_via->GetStart() == aVia->GetStart()) )
  188. {
  189. // delete via
  190. m_Brd->GetRatsnest()->Remove( alt_via );
  191. alt_via->ViewRelease();
  192. alt_via->DeleteStructure();
  193. modified = true;
  194. }
  195. }
  196. return modified;
  197. }
  198. bool TRACKS_CLEANER::clean_vias()
  199. {
  200. bool modified = false;
  201. for( VIA* via = GetFirstVia( m_Brd->m_Track ); via != NULL;
  202. via = GetFirstVia( via->Next() ) )
  203. {
  204. // Correct via m_End defects (if any), should never happen
  205. if( via->GetStart() != via->GetEnd() )
  206. {
  207. wxFAIL_MSG( wxT( "Via with mismatching ends" ) );
  208. via->SetEnd( via->GetStart() );
  209. }
  210. /* Important: these cleanups only do thru hole vias, they don't
  211. * (yet) handle high density interconnects */
  212. if( via->GetViaType() != VIA_THROUGH )
  213. {
  214. modified |= remove_duplicates_of_via( via );
  215. /* To delete through Via on THT pads at same location
  216. * Examine the list of connected pads:
  217. * if one through pad is found, the via can be removed */
  218. for( unsigned ii = 0; ii < via->m_PadsConnected.size(); ++ii )
  219. {
  220. const D_PAD *pad = via->m_PadsConnected[ii];
  221. const LSET all_cu = LSET::AllCuMask();
  222. if( (pad->GetLayerSet() & all_cu) == all_cu )
  223. {
  224. // redundant: delete the via
  225. m_Brd->GetRatsnest()->Remove( via );
  226. via->ViewRelease();
  227. via->DeleteStructure();
  228. modified = true;
  229. break;
  230. }
  231. }
  232. }
  233. }
  234. return modified;
  235. }
  236. /// Utility for checking if a track/via ends on a zone
  237. const ZONE_CONTAINER* TRACKS_CLEANER::zoneForTrackEndpoint( const TRACK *aTrack,
  238. ENDPOINT_T aEndPoint )
  239. {
  240. // Vias are special cased, since they get a layer range, not a single one
  241. LAYER_ID top_layer, bottom_layer;
  242. const VIA* via = dyn_cast<const VIA*>( aTrack );
  243. if( via )
  244. via->LayerPair( &top_layer, &bottom_layer );
  245. else
  246. {
  247. top_layer = aTrack->GetLayer();
  248. bottom_layer = top_layer;
  249. }
  250. return m_Brd->HitTestForAnyFilledArea( aTrack->GetEndPoint( aEndPoint ),
  251. top_layer, bottom_layer, aTrack->GetNetCode() );
  252. }
  253. /** Utility: does the endpoint unconnected processed for one endpoint of one track
  254. * Returns true if the track must be deleted, false if not necessarily */
  255. bool TRACKS_CLEANER::testTrackEndpointDangling( TRACK *aTrack, ENDPOINT_T aEndPoint )
  256. {
  257. bool flag_erase = false;
  258. TRACK* other = aTrack->GetTrack( m_Brd->m_Track, NULL, aEndPoint, true, false );
  259. if( (other == NULL) &&
  260. (zoneForTrackEndpoint( aTrack, aEndPoint ) == NULL) )
  261. flag_erase = true; // Start endpoint is neither on pad, zone or other track
  262. else // segment, via or zone connected to this end
  263. {
  264. // Fill connectivity informations
  265. if( aEndPoint == ENDPOINT_START )
  266. aTrack->start = other;
  267. else
  268. aTrack->end = other;
  269. /* If a via is connected to this end, test if this via has a second item connected.
  270. * If not, remove the current segment (the via would then become
  271. * unconnected and remove on the following pass) */
  272. VIA* via = dyn_cast<VIA*>( other );
  273. if( via )
  274. {
  275. // search for another segment following the via
  276. aTrack->SetState( BUSY, true );
  277. other = via->GetTrack( m_Brd->m_Track, NULL, aEndPoint, true, false );
  278. // There is a via on the start but it goes nowhere
  279. if( (other == NULL) &&
  280. (zoneForTrackEndpoint( via, aEndPoint ) == NULL) )
  281. flag_erase = true;
  282. aTrack->SetState( BUSY, false );
  283. }
  284. }
  285. return flag_erase;
  286. }
  287. /*
  288. * Delete dangling tracks
  289. * Vias:
  290. * If a via is only connected to a dangling track, it also will be removed
  291. */
  292. bool TRACKS_CLEANER::deleteUnconnectedTracks()
  293. {
  294. if( m_Brd->m_Track == NULL )
  295. return false;
  296. bool modified = false;
  297. bool item_erased;
  298. do // Iterate when at least one track is deleted
  299. {
  300. item_erased = false;
  301. TRACK* next_track;
  302. for( TRACK *track = m_Brd->m_Track; track != NULL; track = next_track )
  303. {
  304. next_track = track->Next();
  305. bool flag_erase = false; // Start without a good reason to erase it
  306. /* if a track endpoint is not connected to a pad, test if
  307. * the endpoint is connected to another track or to a zone.
  308. * For via test, an enhancement could be to test if
  309. * connected to 2 items on different layers. Currently
  310. * a via must be connected to 2 items, that can be on the
  311. * same layer */
  312. // Check if there is nothing attached on the start
  313. if( !(track->GetState( START_ON_PAD )) )
  314. flag_erase |= testTrackEndpointDangling( track, ENDPOINT_START );
  315. // Check if there is nothing attached on the end
  316. if( !(track->GetState( END_ON_PAD )) )
  317. flag_erase |= testTrackEndpointDangling( track, ENDPOINT_END );
  318. if( flag_erase )
  319. {
  320. // remove segment from board
  321. m_Brd->GetRatsnest()->Remove( track );
  322. track->ViewRelease();
  323. track->DeleteStructure();
  324. /* keep iterating, because a track connected to the deleted track
  325. * now perhaps is not connected and should be deleted */
  326. item_erased = true;
  327. modified = true;
  328. }
  329. }
  330. } while( item_erased );
  331. return modified;
  332. }
  333. // Delete null length track segments
  334. bool TRACKS_CLEANER::delete_null_segments()
  335. {
  336. TRACK *nextsegment;
  337. bool modified = false;
  338. // Delete null segments
  339. for( TRACK *segment = m_Brd->m_Track; segment; segment = nextsegment )
  340. {
  341. nextsegment = segment->Next();
  342. if( segment->IsNull() ) // Length segment = 0; delete it
  343. {
  344. m_Brd->GetRatsnest()->Remove( segment );
  345. segment->ViewRelease();
  346. segment->DeleteStructure();
  347. modified = true;
  348. }
  349. }
  350. return modified;
  351. }
  352. bool TRACKS_CLEANER::remove_duplicates_of_track( const TRACK *aTrack )
  353. {
  354. bool modified = false;
  355. TRACK *nextsegment;
  356. for( TRACK *other = aTrack->Next(); other; other = nextsegment )
  357. {
  358. nextsegment = other->Next();
  359. // New netcode, break out (can't be there any other)
  360. if( aTrack->GetNetCode() != other->GetNetCode() )
  361. break;
  362. // Must be of the same type, on the same layer and the endpoints
  363. // must be the same (maybe swapped)
  364. if( (aTrack->Type() != other->Type()) &&
  365. (aTrack->GetLayer() != other->GetLayer()) )
  366. {
  367. if( ((aTrack->GetStart() == other->GetStart()) &&
  368. (aTrack->GetEnd() == other->GetEnd())) ||
  369. ((aTrack->GetStart() == other->GetEnd()) &&
  370. (aTrack->GetEnd() == other->GetStart())))
  371. {
  372. m_Brd->GetRatsnest()->Remove( other );
  373. other->ViewRelease();
  374. other->DeleteStructure();
  375. modified = true;
  376. }
  377. }
  378. }
  379. return modified;
  380. }
  381. bool TRACKS_CLEANER::merge_collinear_of_track( TRACK *aSegment )
  382. {
  383. bool merged_this = false;
  384. // *WHY* doesn't C++ have prec and succ (or ++ --) like PASCAL?
  385. for( ENDPOINT_T endpoint = ENDPOINT_START; endpoint <= ENDPOINT_END;
  386. endpoint = ENDPOINT_T( endpoint + 1 ) )
  387. {
  388. // search for a possible segment connected to the current endpoint of the current one
  389. TRACK *other = aSegment->Next();
  390. if( other )
  391. {
  392. other = aSegment->GetTrack( other, NULL, endpoint, true, false );
  393. if( other )
  394. {
  395. // the two segments must have the same width and the other
  396. // cannot be a via
  397. if( (aSegment->GetWidth() == other->GetWidth()) &&
  398. (other->Type() == PCB_TRACE_T) )
  399. {
  400. // There can be only one segment connected
  401. other->SetState( BUSY, true );
  402. TRACK *yet_another = aSegment->GetTrack( m_Brd->m_Track, NULL,
  403. endpoint, true, false );
  404. other->SetState( BUSY, false );
  405. if( !yet_another )
  406. {
  407. // Try to merge them
  408. TRACK *segDelete = mergeCollinearSegmentIfPossible( aSegment,
  409. other, endpoint );
  410. // Merge succesful, the other one has to go away
  411. if( segDelete )
  412. {
  413. m_Brd->GetRatsnest()->Remove( segDelete );
  414. segDelete->ViewRelease();
  415. segDelete->DeleteStructure();
  416. merged_this = true;
  417. }
  418. }
  419. }
  420. }
  421. }
  422. }
  423. return merged_this;
  424. }
  425. // Delete null length segments, and intermediate points ..
  426. bool TRACKS_CLEANER::clean_segments()
  427. {
  428. bool modified = false;
  429. // Easy things first
  430. modified |= delete_null_segments();
  431. // Delete redundant segments, i.e. segments having the same end points and layers
  432. for( TRACK *segment = m_Brd->m_Track; segment; segment = segment->Next() )
  433. modified |= remove_duplicates_of_track( segment );
  434. // merge collinear segments:
  435. TRACK *nextsegment;
  436. for( TRACK *segment = m_Brd->m_Track; segment; segment = nextsegment )
  437. {
  438. nextsegment = segment->Next();
  439. if( segment->Type() == PCB_TRACE_T )
  440. {
  441. bool merged_this = merge_collinear_of_track( segment );
  442. modified |= merged_this;
  443. if( merged_this ) // The current segment was modified, retry to merge it again
  444. nextsegment = segment->Next();
  445. }
  446. }
  447. return modified;
  448. }
  449. /* Utility: check for parallelism between two segments */
  450. static bool parallelism_test( int dx1, int dy1, int dx2, int dy2 )
  451. {
  452. /* The following condition list is ugly and repetitive, but I have
  453. * not a better way to express clearly the trivial cases. Hope the
  454. * compiler optimize it better than always doing the product
  455. * below... */
  456. // test for vertical alignment (easy to handle)
  457. if( dx1 == 0 )
  458. return dx2 == 0;
  459. if( dx2 == 0 )
  460. return dx1 == 0;
  461. // test for horizontal alignment (easy to handle)
  462. if( dy1 == 0 )
  463. return dy2 == 0;
  464. if( dy2 == 0 )
  465. return dy1 == 0;
  466. /* test for alignment in other cases: Do the usual cross product test
  467. * (the same as testing the slope, but without a division) */
  468. return ((double)dy1 * dx2 == (double)dx1 * dy2);
  469. }
  470. /** Function used by clean_segments.
  471. * Test if aTrackRef and aCandidate (which must have a common end) are collinear.
  472. * and see if the common point is not on a pad (i.e. if this common point can be removed).
  473. * the ending point of aTrackRef is the start point (aEndType == START)
  474. * or the end point (aEndType != START)
  475. * flags START_ON_PAD and END_ON_PAD must be set before calling this function
  476. * if the common point can be deleted, this function
  477. * change the common point coordinate of the aTrackRef segm
  478. * (and therefore connect the 2 other ending points)
  479. * and return aCandidate (which can be deleted).
  480. * else return NULL
  481. */
  482. TRACK* TRACKS_CLEANER::mergeCollinearSegmentIfPossible( TRACK* aTrackRef, TRACK* aCandidate,
  483. ENDPOINT_T aEndType )
  484. {
  485. // First of all, they must be of the same width and must be both actual tracks
  486. if( (aTrackRef->GetWidth() != aCandidate->GetWidth()) ||
  487. (aTrackRef->Type() != PCB_TRACE_T) ||
  488. (aCandidate->Type() != PCB_TRACE_T) )
  489. return NULL;
  490. // Trivial case: exactly the same track
  491. if( ( aTrackRef->GetStart() == aCandidate->GetStart() ) &&
  492. ( aTrackRef->GetEnd() == aCandidate->GetEnd() ) )
  493. return aCandidate;
  494. if( ( aTrackRef->GetStart() == aCandidate->GetEnd() ) &&
  495. ( aTrackRef->GetEnd() == aCandidate->GetStart() ) )
  496. return aCandidate;
  497. // Weed out non-parallel tracks
  498. if ( !parallelism_test( aTrackRef->GetEnd().x - aTrackRef->GetStart().x,
  499. aTrackRef->GetEnd().y - aTrackRef->GetStart().y,
  500. aCandidate->GetEnd().x - aCandidate->GetStart().x,
  501. aCandidate->GetEnd().y - aCandidate->GetStart().y ) )
  502. return NULL;
  503. /* Here we have 2 aligned segments:
  504. * We must change the pt_ref common point only if not on a pad
  505. * (this function) is called when there is only 2 connected segments,
  506. * and if this point is not on a pad, it can be removed and the 2 segments will be merged
  507. */
  508. if( aEndType == ENDPOINT_START )
  509. {
  510. // We do not have a pad, which is a always terminal point for a track
  511. if( aTrackRef->GetState( START_ON_PAD) )
  512. return NULL;
  513. /* change the common point coordinate of pt_segm to use the other point
  514. * of pt_segm (pt_segm will be removed later) */
  515. if( aTrackRef->GetStart() == aCandidate->GetStart() )
  516. {
  517. aTrackRef->SetStart( aCandidate->GetEnd());
  518. aTrackRef->start = aCandidate->end;
  519. aTrackRef->SetState( START_ON_PAD, aCandidate->GetState( END_ON_PAD) );
  520. aTrackRef->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
  521. return aCandidate;
  522. }
  523. else
  524. {
  525. aTrackRef->SetStart( aCandidate->GetStart() );
  526. aTrackRef->start = aCandidate->start;
  527. aTrackRef->SetState( START_ON_PAD, aCandidate->GetState( START_ON_PAD) );
  528. aTrackRef->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
  529. return aCandidate;
  530. }
  531. }
  532. else // aEndType == END
  533. {
  534. // We do not have a pad, which is a always terminal point for a track
  535. if( aTrackRef->GetState( END_ON_PAD) )
  536. return NULL;
  537. /* change the common point coordinate of pt_segm to use the other point
  538. * of pt_segm (pt_segm will be removed later) */
  539. if( aTrackRef->GetEnd() == aCandidate->GetStart() )
  540. {
  541. aTrackRef->SetEnd( aCandidate->GetEnd() );
  542. aTrackRef->end = aCandidate->end;
  543. aTrackRef->SetState( END_ON_PAD, aCandidate->GetState( END_ON_PAD) );
  544. aTrackRef->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
  545. return aCandidate;
  546. }
  547. else
  548. {
  549. aTrackRef->SetEnd( aCandidate->GetStart() );
  550. aTrackRef->end = aCandidate->start;
  551. aTrackRef->SetState( END_ON_PAD, aCandidate->GetState( START_ON_PAD) );
  552. aTrackRef->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
  553. return aCandidate;
  554. }
  555. }
  556. return NULL;
  557. }
  558. bool PCB_EDIT_FRAME::RemoveMisConnectedTracks()
  559. {
  560. /* finds all track segments which are mis-connected (to more than one net).
  561. * When such a bad segment is found, it is flagged to be removed.
  562. * All tracks having at least one flagged segment are removed.
  563. */
  564. TRACK* segment;
  565. TRACK* other;
  566. TRACK* next;
  567. int net_code_s, net_code_e;
  568. bool isModified = false;
  569. for( segment = GetBoard()->m_Track; segment; segment = (TRACK*) segment->Next() )
  570. {
  571. segment->SetState( FLAG0, false );
  572. // find the netcode for segment using anything connected to the "start" of "segment"
  573. net_code_s = -1;
  574. if( segment->start && segment->start->Type()==PCB_PAD_T )
  575. {
  576. // get the netcode of the pad to propagate.
  577. net_code_s = ((D_PAD*)(segment->start))->GetNetCode();
  578. }
  579. else
  580. {
  581. other = segment->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_START, false, false );
  582. if( other )
  583. net_code_s = other->GetNetCode();
  584. }
  585. if( net_code_s < 0 )
  586. continue; // the "start" of segment is not connected
  587. // find the netcode for segment using anything connected to the "end" of "segment"
  588. net_code_e = -1;
  589. if( segment->end && segment->end->Type()==PCB_PAD_T )
  590. {
  591. net_code_e = ((D_PAD*)(segment->end))->GetNetCode();
  592. }
  593. else
  594. {
  595. other = segment->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_END, false, false );
  596. if( other )
  597. net_code_e = other->GetNetCode();
  598. }
  599. if( net_code_e < 0 )
  600. continue; // the "end" of segment is not connected
  601. // Netcodes do not agree, so mark the segment as "to be removed"
  602. if( net_code_s != net_code_e )
  603. {
  604. segment->SetState( FLAG0, true );
  605. }
  606. }
  607. // Remove tracks having a flagged segment
  608. for( segment = GetBoard()->m_Track; segment; segment = next )
  609. {
  610. next = (TRACK*) segment->Next();
  611. if( segment->GetState( FLAG0 ) ) // Segment is flagged to be removed
  612. {
  613. segment->SetState( FLAG0, false );
  614. isModified = true;
  615. GetBoard()->m_Status_Pcb = 0;
  616. Remove_One_Track( NULL, segment );
  617. // the current segment is deleted,
  618. // we do not know the next "not yet tested" segment,
  619. // so restart to the beginning
  620. next = GetBoard()->m_Track;
  621. }
  622. }
  623. return isModified;
  624. }