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.

803 lines
25 KiB

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