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.

1254 lines
39 KiB

18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
  1. /*
  2. * This program source code file is part of KICAD, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2004-2007 Jean-Pierre Charras, jean-pierre.charras@inpg.fr
  5. * Copyright (C) 2007 Dick Hollenbeck, dick@softplc.com
  6. * Copyright (C) 2007 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. /* DRC control */
  27. /****************************/
  28. #include "fctsys.h"
  29. #include "gr_basic.h"
  30. #include "common.h"
  31. #include "pcbnew.h"
  32. #include "autorout.h"
  33. #include "trigo.h"
  34. #include "protos.h"
  35. #include "drc_stuff.h"
  36. #include "dialog_drc.cpp"
  37. /******************************************************/
  38. void WinEDA_PcbFrame::Install_Test_DRC_Frame( wxDC* DC )
  39. /******************************************************/
  40. {
  41. m_drc->ShowDialog();
  42. }
  43. void DRC::ShowDialog()
  44. {
  45. if( !m_ui )
  46. {
  47. m_ui = new DrcDialog( this, m_mainWindow );
  48. updatePointers();
  49. // copy data retained in this DRC object into the m_ui DrcPanel:
  50. PutValueInLocalUnits( *m_ui->m_SetClearance, g_DesignSettings.m_TrackClearence,
  51. m_mainWindow->m_InternalUnits );;
  52. m_ui->m_Pad2PadTestCtrl->SetValue( m_doPad2PadTest );
  53. m_ui->m_ZonesTestCtrl->SetValue( m_doZonesTest );
  54. m_ui->m_UnconnectedTestCtrl->SetValue( m_doUnconnectedTest );
  55. m_ui->m_CreateRptCtrl->SetValue( m_doCreateRptFile );
  56. m_ui->m_RptFilenameCtrl->SetValue( m_rptFilename );
  57. }
  58. else
  59. updatePointers();
  60. m_ui->Show(true);
  61. }
  62. void DRC::DestroyDialog( int aReason )
  63. {
  64. if( m_ui )
  65. {
  66. if( aReason == wxID_OK )
  67. {
  68. // if user clicked OK, save his choices in this DRC object.
  69. m_doCreateRptFile = m_ui->m_CreateRptCtrl->GetValue();
  70. m_doPad2PadTest = m_ui->m_Pad2PadTestCtrl->GetValue();
  71. m_doZonesTest = m_ui->m_ZonesTestCtrl->GetValue();
  72. m_doUnconnectedTest = m_ui->m_UnconnectedTestCtrl->GetValue();
  73. m_rptFilename = m_ui->m_RptFilenameCtrl->GetValue();
  74. }
  75. m_ui->Destroy();
  76. m_ui = 0;
  77. }
  78. }
  79. DRC::DRC( WinEDA_PcbFrame* aPcbWindow )
  80. {
  81. m_mainWindow = aPcbWindow;
  82. m_drawPanel = aPcbWindow->DrawPanel;
  83. m_pcb = aPcbWindow->m_Pcb;
  84. m_ui = 0;
  85. // establish initial values for everything:
  86. m_doPad2PadTest = true;
  87. m_doUnconnectedTest = true;
  88. m_doZonesTest = false;
  89. m_doCreateRptFile = false;
  90. // m_rptFilename set to empty by its constructor
  91. m_currentMarker = 0;
  92. m_spotcx = 0;
  93. m_spotcy = 0;
  94. m_finx = 0;
  95. m_finy = 0;
  96. m_segmAngle = 0;
  97. m_segmLength = 0;
  98. m_xcliplo = 0;
  99. m_ycliplo = 0;
  100. m_xcliphi = 0;
  101. m_ycliphi = 0;
  102. m_drawPanel = 0;
  103. }
  104. DRC::~DRC()
  105. {
  106. // maybe someday look at pointainer.h <- google for "pointainer.h"
  107. for( unsigned i=0; i<m_unconnected.size(); ++i )
  108. delete m_unconnected[i];
  109. }
  110. /***********************************************************************/
  111. int DRC::Drc( TRACK* aRefSegm, TRACK* aList )
  112. /***********************************************************************/
  113. {
  114. updatePointers();
  115. if( !doTrackDrc( aRefSegm, aList ) )
  116. {
  117. wxASSERT( m_currentMarker );
  118. m_currentMarker->Display_Infos( m_mainWindow );
  119. return BAD_DRC;
  120. }
  121. return OK_DRC;
  122. }
  123. void DRC::RunTests()
  124. {
  125. // someone should have cleared the two lists before calling this.
  126. // test pad to pad clearances, nothing to do with tracks, vias or zones.
  127. if( m_doPad2PadTest )
  128. testPad2Pad();
  129. // test track and via clearances to other tracks, pads, and vias
  130. testTracks();
  131. // test zone clearances to other zones, pads, tracks, and vias
  132. if( m_doZonesTest )
  133. testZones();
  134. // find and gather unconnected pads.
  135. if( m_doUnconnectedTest )
  136. testUnconnected();
  137. // update the m_ui listboxes
  138. updatePointers();
  139. }
  140. /***************************************************************/
  141. void DRC::ListUnconnectedPads()
  142. /***************************************************************/
  143. {
  144. testUnconnected();
  145. // update the m_ui listboxes
  146. updatePointers();
  147. }
  148. void DRC::updatePointers()
  149. {
  150. // update my pointers, m_mainWindow is the only unchangable one
  151. m_drawPanel = m_mainWindow->DrawPanel;
  152. m_pcb = m_mainWindow->m_Pcb;
  153. if ( m_ui ) // Use diag list boxes only in DRC dialog
  154. {
  155. m_ui->m_ClearanceListBox->SetList( new DRC_LIST_MARKERS( m_pcb ) );
  156. m_ui->m_UnconnectedListBox->SetList( new DRC_LIST_UNCONNECTED( &m_unconnected ) );
  157. }
  158. }
  159. void DRC::testTracks()
  160. {
  161. for( TRACK* segm = m_pcb->m_Track; segm && segm->Next(); segm=segm->Next() )
  162. {
  163. if( !doTrackDrc( segm, segm->Next() ) )
  164. {
  165. wxASSERT( m_currentMarker );
  166. m_pcb->Add( m_currentMarker );
  167. m_currentMarker = 0;
  168. }
  169. }
  170. }
  171. void DRC::testPad2Pad()
  172. {
  173. LISTE_PAD* pad_list_start = CreateSortedPadListByXCoord( m_pcb );
  174. LISTE_PAD* pad_list_limit = &pad_list_start[m_pcb->m_NbPads];
  175. LISTE_PAD* ppad;
  176. // find the max size of the pads (used to stop the test)
  177. int max_size = 0;
  178. for( ppad = pad_list_start; ppad<pad_list_limit; ppad++ )
  179. {
  180. D_PAD* pad = *ppad;
  181. if( pad->m_Rayon > max_size )
  182. max_size = pad->m_Rayon;
  183. }
  184. // Test the pads
  185. for( ppad = pad_list_start; ppad<pad_list_limit; ppad++ )
  186. {
  187. D_PAD* pad = *ppad;
  188. if( !doPadToPadsDrc( pad, ppad, pad_list_limit, max_size ) )
  189. {
  190. wxASSERT( m_currentMarker );
  191. m_pcb->Add( m_currentMarker );
  192. m_currentMarker = 0;
  193. }
  194. }
  195. free( pad_list_start );
  196. }
  197. void DRC::testUnconnected()
  198. {
  199. if( (m_pcb->m_Status_Pcb & LISTE_CHEVELU_OK) == 0 )
  200. {
  201. wxClientDC dc( m_mainWindow->DrawPanel );
  202. m_mainWindow->Compile_Ratsnest( &dc, TRUE );
  203. }
  204. if( m_pcb->m_Ratsnest == NULL )
  205. return;
  206. CHEVELU* rat = m_pcb->m_Ratsnest;
  207. for( int i=0; i<m_pcb->GetNumRatsnests(); ++i, ++rat )
  208. {
  209. if( (rat->status & CH_ACTIF) == 0 )
  210. continue;
  211. D_PAD* padStart = rat->pad_start;
  212. D_PAD* padEnd = rat->pad_end;
  213. DRC_ITEM* uncItem = new DRC_ITEM( DRCE_UNCONNECTED_PADS, padStart->GetPosition(),
  214. padStart->MenuText(m_pcb), padEnd->MenuText(m_pcb),
  215. padStart->GetPosition(), padEnd->GetPosition() );
  216. m_unconnected.push_back( uncItem );
  217. }
  218. }
  219. void DRC::testZones()
  220. {
  221. TRACK* zoneSeg;
  222. /* this was for display purposes, don't know that we need it anymore
  223. m_pcb->m_NbSegmZone = 0;
  224. for( zoneSeg = m_pcb->m_Zone; zoneSeg; zoneSeg = zoneSeg->Next() )
  225. ++m_pcb->m_NbSegmZone;
  226. */
  227. for( zoneSeg = m_pcb->m_Zone; zoneSeg && zoneSeg->Next(); zoneSeg=zoneSeg->Next() )
  228. {
  229. // Test zoneSeg with other zone segments and with all pads
  230. if( !doTrackDrc( zoneSeg, zoneSeg->Next() ) )
  231. {
  232. wxASSERT( m_currentMarker );
  233. m_pcb->Add( m_currentMarker );
  234. m_currentMarker = 0;
  235. }
  236. // Test zoneSeg with all track segments
  237. int tmp = m_pcb->m_NbPads;
  238. m_pcb->m_NbPads = 0; // Pads already tested: disable pad test
  239. bool rc = doTrackDrc( zoneSeg, m_pcb->m_Track );
  240. m_pcb->m_NbPads = tmp;
  241. if( !rc )
  242. {
  243. wxASSERT( m_currentMarker );
  244. m_pcb->Add( m_currentMarker );
  245. m_currentMarker = 0;
  246. }
  247. }
  248. }
  249. MARKER* DRC::fillMarker( TRACK* aTrack, BOARD_ITEM* aItem, int aErrorCode, MARKER* fillMe )
  250. {
  251. wxString textA = aTrack->MenuText( m_pcb );
  252. wxString textB;
  253. wxPoint position;
  254. wxPoint posB;
  255. if( aItem ) // aItem might be NULL
  256. {
  257. textB = aItem->MenuText( m_pcb );
  258. posB = aItem->GetPosition();
  259. if( aItem->Type() == TYPEPAD )
  260. position = aItem->GetPosition();
  261. else if( aItem->Type() == TYPEVIA )
  262. position = aItem->GetPosition();
  263. else if( aItem->Type() == TYPETRACK )
  264. {
  265. TRACK* track = (TRACK*) aItem;
  266. wxPoint endPos = track->m_End;
  267. // either of aItem's start or end will be used for the marker position
  268. // first assume start, then switch at end if needed. decision made on
  269. // distance from end of aTrack.
  270. position = track->m_Start;
  271. double dToEnd = hypot( endPos.x - aTrack->m_End.x,
  272. endPos.y - aTrack->m_End.y );
  273. double dToStart = hypot( position.x - aTrack->m_End.x,
  274. position.y - aTrack->m_End.y );
  275. if( dToEnd < dToStart )
  276. position = endPos;
  277. }
  278. }
  279. else
  280. position = aTrack->GetPosition();
  281. if( fillMe )
  282. fillMe->SetData( aErrorCode, position,
  283. textA, aTrack->GetPosition(),
  284. textB, posB );
  285. else
  286. fillMe = new MARKER( aErrorCode, position,
  287. textA, aTrack->GetPosition(),
  288. textB, posB );
  289. return fillMe;
  290. }
  291. MARKER* DRC::fillMarker( D_PAD* aPad, D_PAD* bPad, int aErrorCode, MARKER* fillMe )
  292. {
  293. wxString textA = aPad->MenuText( m_pcb );
  294. wxString textB = bPad->MenuText( m_pcb );
  295. wxPoint posA = aPad->GetPosition();
  296. wxPoint posB = bPad->GetPosition();
  297. if( fillMe )
  298. fillMe->SetData( aErrorCode, posA, textA, posA, textB, posB );
  299. else
  300. fillMe = new MARKER( aErrorCode, posA, textA, posA, textB, posB );
  301. return fillMe;
  302. }
  303. /***********************************************************************/
  304. bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart )
  305. /***********************************************************************/
  306. {
  307. TRACK* track;
  308. int dx, dy; // utilise pour calcul des dim x et dim y des segments
  309. int w_dist;
  310. int layerMask;
  311. int net_code_ref;
  312. int org_X, org_Y; // Origine sur le PCB des axes du repere centre sur
  313. // l'origine du segment de reference
  314. wxPoint shape_pos;
  315. org_X = aRefSeg->m_Start.x;
  316. org_Y = aRefSeg->m_Start.y;
  317. m_finx = dx = aRefSeg->m_End.x - org_X;
  318. m_finy = dy = aRefSeg->m_End.y - org_Y;
  319. layerMask = aRefSeg->ReturnMaskLayer();
  320. net_code_ref = aRefSeg->GetNet();
  321. m_segmAngle = 0;
  322. // @todo: is this necessary?
  323. /**************************************************************/
  324. /* Phase 0 : test if via's hole is bigger than its diameter : */
  325. /**************************************************************/
  326. if( aRefSeg->Type() == TYPEVIA )
  327. {
  328. // This test seems necessary since the dialog box that displays the
  329. // desired via hole size and width does not enforce a hole size smaller
  330. // than the via's diameter.
  331. if( aRefSeg->m_Drill > aRefSeg->m_Width )
  332. {
  333. m_currentMarker = fillMarker( aRefSeg, NULL,
  334. DRCE_VIA_HOLE_BIGGER, m_currentMarker );
  335. return false;
  336. }
  337. }
  338. // for a non horizontal or vertical segment Compute the segment angle
  339. // in tenths of degrees and its length
  340. if( dx || dy )
  341. {
  342. // Compute the segment angle in 0,1 degrees
  343. m_segmAngle = ArcTangente( dy, dx );
  344. // Compute the segment length: we build an equivalent rotated segment,
  345. // this segment is horizontal, therefore dx = length
  346. RotatePoint( &dx, &dy, m_segmAngle ); // dx = length, dy = 0
  347. }
  348. m_segmLength = dx;
  349. /******************************************/
  350. /* Phase 1 : test DRC track to pads : */
  351. /******************************************/
  352. D_PAD pseudo_pad( (MODULE*) NULL ); // construct this once outside following loop
  353. // Compute the min distance to pads
  354. w_dist = aRefSeg->m_Width >> 1;
  355. for( int ii=0; ii<m_pcb->m_NbPads; ++ii )
  356. {
  357. D_PAD* pad = m_pcb->m_Pads[ii];
  358. /* No problem if pads are on an other layer,
  359. * But if a drill hole exists (a pad on a single layer can have a hole!)
  360. * we must test the hole
  361. */
  362. if( (pad->m_Masque_Layer & layerMask ) == 0 )
  363. {
  364. /* We must test the pad hole. In order to use the function "checkClearanceSegmToPad",
  365. * a pseudo pad is used, with a shape and a size like the hole
  366. */
  367. if( pad->m_Drill.x == 0 )
  368. continue;
  369. pseudo_pad.m_Size = pad->m_Drill;
  370. pseudo_pad.SetPosition( pad->GetPosition() );
  371. pseudo_pad.m_PadShape = pad->m_DrillShape;
  372. pseudo_pad.m_Orient = pad->m_Orient;
  373. pseudo_pad.ComputeRayon(); // compute the radius
  374. m_spotcx = pseudo_pad.GetPosition().x - org_X;
  375. m_spotcy = pseudo_pad.GetPosition().y - org_Y;
  376. if( !checkClearanceSegmToPad( &pseudo_pad, w_dist,
  377. g_DesignSettings.m_TrackClearence ) )
  378. {
  379. m_currentMarker = fillMarker( aRefSeg, pad,
  380. DRCE_TRACK_NEAR_THROUGH_HOLE, m_currentMarker );
  381. return false;
  382. }
  383. continue;
  384. }
  385. /* The pad must be in a net (i.e pt_pad->GetNet() != 0 )
  386. * but no problem if the pad netcode is the current netcode (same net)
  387. */
  388. if( pad->GetNet() && // the pad must be connected
  389. net_code_ref == pad->GetNet() ) // the pad net is the same as current net -> Ok
  390. continue;
  391. // DRC for the pad
  392. shape_pos = pad->ReturnShapePos();
  393. m_spotcx = shape_pos.x - org_X;
  394. m_spotcy = shape_pos.y - org_Y;
  395. if( !checkClearanceSegmToPad( pad, w_dist, g_DesignSettings.m_TrackClearence ) )
  396. {
  397. m_currentMarker = fillMarker( aRefSeg, pad,
  398. DRCE_TRACK_NEAR_PAD, m_currentMarker );
  399. return false;
  400. }
  401. }
  402. /***********************************************/
  403. /* Phase 2: test DRC with other track segments */
  404. /***********************************************/
  405. // At this point the reference segment is the X axis
  406. // Test the reference segment with other track segments
  407. for( track=aStart; track; track=track->Next() )
  408. {
  409. // coord des extremites du segment teste dans le repere modifie
  410. int x0;
  411. int y0;
  412. int xf;
  413. int yf;
  414. // No problem if segments have the same net code:
  415. if( net_code_ref == track->GetNet() )
  416. continue;
  417. // No problem if segment are on different layers :
  418. if( ( layerMask & track->ReturnMaskLayer() ) == 0 )
  419. continue;
  420. // the minimum distance = clearance plus half the reference track
  421. // width plus half the other track's width
  422. w_dist = aRefSeg->m_Width >> 1;
  423. w_dist += track->m_Width >> 1;
  424. w_dist += g_DesignSettings.m_TrackClearence;
  425. // If the reference segment is a via, we test it here
  426. if( aRefSeg->Type() == TYPEVIA )
  427. {
  428. int orgx, orgy; // origine du repere d'axe X = segment a comparer
  429. int angle = 0; // angle du segment a tester;
  430. orgx = track->m_Start.x;
  431. orgy = track->m_Start.y;
  432. dx = track->m_End.x - orgx;
  433. dy = track->m_End.y - orgy;
  434. x0 = aRefSeg->m_Start.x - orgx;
  435. y0 = aRefSeg->m_Start.y - orgy;
  436. if( track->Type() == TYPEVIA )
  437. {
  438. // Test distance between two vias
  439. if( (int) hypot( x0, y0 ) < w_dist )
  440. {
  441. m_currentMarker = fillMarker( aRefSeg, track,
  442. DRCE_VIA_NEAR_VIA, m_currentMarker );
  443. return false;
  444. }
  445. }
  446. else // test via to segment
  447. {
  448. // Compute l'angle
  449. angle = ArcTangente( dy, dx );
  450. // Compute new coordinates ( the segment become horizontal)
  451. RotatePoint( &dx, &dy, angle );
  452. RotatePoint( &x0, &y0, angle );
  453. if( !checkMarginToCircle( x0, y0, w_dist, dx ) )
  454. {
  455. m_currentMarker = fillMarker( aRefSeg, track,
  456. DRCE_VIA_NEAR_TRACK, m_currentMarker );
  457. return false;
  458. }
  459. }
  460. continue;
  461. }
  462. /* We compute x0,y0, xf,yf = starting and ending point coordinates for the segment to test
  463. * in the new axis : the new X axis is the reference segment
  464. * We must translate and rotate the segment to test
  465. */
  466. x0 = track->m_Start.x - org_X;
  467. y0 = track->m_Start.y - org_Y;
  468. xf = track->m_End.x - org_X;
  469. yf = track->m_End.y - org_Y;
  470. RotatePoint( &x0, &y0, m_segmAngle );
  471. RotatePoint( &xf, &yf, m_segmAngle );
  472. if( track->Type() == TYPEVIA )
  473. {
  474. if( checkMarginToCircle( x0, y0, w_dist, m_segmLength ) )
  475. continue;
  476. m_currentMarker = fillMarker( aRefSeg, track,
  477. DRCE_TRACK_NEAR_VIA, m_currentMarker );
  478. return false;
  479. }
  480. /* We have changed axis:
  481. * the reference segment is Horizontal.
  482. * 3 cases : the segment to test can be parallel, perpendicular or have an other direction
  483. */
  484. if( y0 == yf ) // parallel segments
  485. {
  486. if( abs( y0 ) >= w_dist )
  487. continue;
  488. if( x0 > xf )
  489. EXCHG( x0, xf ); /* pour que x0 <= xf */
  490. if( x0 > (-w_dist) && x0 < (m_segmLength + w_dist) ) /* possible error drc */
  491. {
  492. /* Fine test : we consider the rounded shape of the ends */
  493. if( x0 >= 0 && x0 <= m_segmLength )
  494. {
  495. m_currentMarker = fillMarker( aRefSeg, track,
  496. DRCE_TRACK_ENDS1, m_currentMarker );
  497. return false;
  498. }
  499. if( !checkMarginToCircle( x0, y0, w_dist, m_segmLength ) )
  500. {
  501. m_currentMarker = fillMarker( aRefSeg, track,
  502. DRCE_TRACK_ENDS2, m_currentMarker );
  503. return false;
  504. }
  505. }
  506. if( xf > (-w_dist) && xf < (m_segmLength + w_dist) )
  507. {
  508. /* Fine test : we consider the rounded shape of the ends */
  509. if( xf >= 0 && xf <= m_segmLength )
  510. {
  511. m_currentMarker = fillMarker( aRefSeg, track,
  512. DRCE_TRACK_ENDS3, m_currentMarker );
  513. return false;
  514. }
  515. if( !checkMarginToCircle( xf, yf, w_dist, m_segmLength ) )
  516. {
  517. m_currentMarker = fillMarker( aRefSeg, track,
  518. DRCE_TRACK_ENDS4, m_currentMarker );
  519. return false;
  520. }
  521. }
  522. if( x0 <=0 && xf >= 0 )
  523. {
  524. m_currentMarker = fillMarker( aRefSeg, track,
  525. DRCE_TRACK_UNKNOWN1, m_currentMarker );
  526. return false;
  527. }
  528. }
  529. else if( x0 == xf ) // perpendicular segments
  530. {
  531. if( ( x0 <= (-w_dist) ) || ( x0 >= (m_segmLength + w_dist) ) )
  532. continue;
  533. // Test if segments are crossing
  534. if( y0 > yf )
  535. EXCHG( y0, yf );
  536. if( (y0 < 0) && (yf > 0) )
  537. {
  538. m_currentMarker = fillMarker( aRefSeg, track,
  539. DRCE_TRACKS_CROSSING, m_currentMarker );
  540. return false;
  541. }
  542. // At this point the drc error is due to an end near a reference segm end
  543. if( !checkMarginToCircle( x0, y0, w_dist, m_segmLength ) )
  544. {
  545. m_currentMarker = fillMarker( aRefSeg, track,
  546. DRCE_ENDS_PROBLEM1, m_currentMarker );
  547. return false;
  548. }
  549. if( !checkMarginToCircle( xf, yf, w_dist, m_segmLength ) )
  550. {
  551. m_currentMarker = fillMarker( aRefSeg, track,
  552. DRCE_ENDS_PROBLEM2, m_currentMarker );
  553. return false;
  554. }
  555. }
  556. else // segments quelconques entre eux */
  557. {
  558. // calcul de la "surface de securite du segment de reference
  559. // First rought 'and fast) test : the track segment is like a rectangle
  560. m_xcliplo = m_ycliplo = -w_dist;
  561. m_xcliphi = m_segmLength + w_dist;
  562. m_ycliphi = w_dist;
  563. // A fine test is needed because a serment is not exactly a
  564. // rectangle, it has rounded ends
  565. if( !checkLine( x0, y0, xf, yf ) )
  566. {
  567. /* 2eme passe : the track has rounded ends.
  568. * we must a fine test for each rounded end and the
  569. * rectangular zone
  570. */
  571. m_xcliplo = 0;
  572. m_xcliphi = m_segmLength;
  573. if( !checkLine( x0, y0, xf, yf ) )
  574. {
  575. m_currentMarker = fillMarker( aRefSeg, track,
  576. DRCE_ENDS_PROBLEM3, m_currentMarker );
  577. return false;
  578. }
  579. else // The drc error is due to the starting or the ending point of the reference segment
  580. {
  581. // Test the starting and the ending point
  582. int angle, rx0, ry0, rxf, ryf;
  583. x0 = track->m_Start.x;
  584. y0 = track->m_Start.y;
  585. xf = track->m_End.x;
  586. yf = track->m_End.y;
  587. dx = xf - x0;
  588. dy = yf - y0;
  589. /* Compute the segment orientation (angle) en 0,1 degre */
  590. angle = ArcTangente( dy, dx );
  591. /* Compute the segment lenght: dx = longueur */
  592. RotatePoint( &dx, &dy, angle );
  593. /* Comute the reference segment coordinates relatives to a
  594. * X axis = current tested segment
  595. */
  596. rx0 = aRefSeg->m_Start.x - x0;
  597. ry0 = aRefSeg->m_Start.y - y0;
  598. rxf = aRefSeg->m_End.x - x0;
  599. ryf = aRefSeg->m_End.y - y0;
  600. RotatePoint( &rx0, &ry0, angle );
  601. RotatePoint( &rxf, &ryf, angle );
  602. if( !checkMarginToCircle( rx0, ry0, w_dist, dx ) )
  603. {
  604. m_currentMarker = fillMarker( aRefSeg, track,
  605. DRCE_ENDS_PROBLEM4, m_currentMarker );
  606. return false;
  607. }
  608. if( !checkMarginToCircle( rxf, ryf, w_dist, dx ) )
  609. {
  610. m_currentMarker = fillMarker( aRefSeg, track,
  611. DRCE_ENDS_PROBLEM5, m_currentMarker );
  612. return false;
  613. }
  614. }
  615. }
  616. }
  617. }
  618. return true;
  619. }
  620. /*****************************************************************************/
  621. bool DRC::doPadToPadsDrc( D_PAD* aRefPad, LISTE_PAD* aStart, LISTE_PAD* aEnd,
  622. int max_size )
  623. /*****************************************************************************/
  624. {
  625. int layerMask = aRefPad->m_Masque_Layer & ALL_CU_LAYERS;
  626. int x_limite = max_size + g_DesignSettings.m_TrackClearence +
  627. aRefPad->m_Rayon + aRefPad->GetPosition().x;
  628. for( LISTE_PAD* pad_list=aStart; pad_list<aEnd; ++pad_list )
  629. {
  630. D_PAD* pad = *pad_list;
  631. if( pad == aRefPad )
  632. continue;
  633. /* We can stop the test when pad->m_Pos.x > x_limite
  634. * because the list is sorted by X values */
  635. if( pad->m_Pos.x > x_limite )
  636. break;
  637. /* No probleme if pads are on different copper layers */
  638. if( (pad->m_Masque_Layer & layerMask ) == 0 )
  639. continue;
  640. /* The pad must be in a net (i.e pt_pad->GetNet() != 0 ),
  641. * But no problem if pads have the same netcode (same net)*/
  642. if( pad->GetNet() && (aRefPad->GetNet() == pad->GetNet()) )
  643. continue;
  644. /* No problem if pads are from the same footprint
  645. * and have the same pad number ( equivalent pads ) */
  646. if( (pad->m_Parent == aRefPad->m_Parent) && (pad->m_NumPadName == aRefPad->m_NumPadName) )
  647. continue;
  648. if( !checkClearancePadToPad( aRefPad, pad, g_DesignSettings.m_TrackClearence ) )
  649. {
  650. // here we have a drc error!
  651. m_currentMarker = fillMarker( aRefPad, pad,
  652. DRCE_PAD_NEAR_PAD1, m_currentMarker );
  653. return false;
  654. }
  655. }
  656. return true;
  657. }
  658. /**************************************************************************************/
  659. bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, const int dist_min )
  660. /***************************************************************************************/
  661. {
  662. wxPoint rel_pos;
  663. int dist;;
  664. wxPoint shape_pos;
  665. int pad_angle;
  666. rel_pos = aPad->ReturnShapePos();
  667. shape_pos = aRefPad->ReturnShapePos();
  668. // rel_pos is pad position relative to the aRefPad position
  669. rel_pos.x -= shape_pos.x;
  670. rel_pos.y -= shape_pos.y;
  671. dist = (int) hypot( rel_pos.x, rel_pos.y );
  672. bool diag = true;
  673. /* tst rapide: si les cercles exinscrits sont distants de dist_min au moins,
  674. * il n'y a pas de risque: */
  675. if( (dist - aRefPad->m_Rayon - aPad->m_Rayon) >= dist_min )
  676. goto exit;
  677. /* Ici les pads sont proches et les cercles exinxcrits sont trop proches
  678. * Selon les formes relatives il peut y avoir ou non erreur */
  679. bool swap_pads;
  680. swap_pads = false;
  681. if( (aRefPad->m_PadShape != PAD_CIRCLE) && (aPad->m_PadShape == PAD_CIRCLE) )
  682. swap_pads = true;
  683. else if( (aRefPad->m_PadShape != PAD_OVAL) && (aPad->m_PadShape == PAD_OVAL) )
  684. swap_pads = true;
  685. if( swap_pads )
  686. {
  687. EXCHG( aRefPad, aPad );
  688. rel_pos.x = -rel_pos.x;
  689. rel_pos.y = -rel_pos.y;
  690. }
  691. switch( aRefPad->m_PadShape )
  692. {
  693. case PAD_CIRCLE: // aRefPad is like a track segment with a null lenght
  694. m_segmLength = 0;
  695. m_segmAngle = 0;
  696. m_finx = m_finy = 0;
  697. m_spotcx = rel_pos.x;
  698. m_spotcy = rel_pos.y;
  699. diag = checkClearanceSegmToPad( aPad, aRefPad->m_Rayon, dist_min );
  700. break;
  701. case PAD_RECT:
  702. RotatePoint( &rel_pos.x, &rel_pos.y, aRefPad->m_Orient );
  703. pad_angle = aRefPad->m_Orient + aPad->m_Orient; // pad_angle = pad orient relative to the aRefPad orient
  704. NORMALIZE_ANGLE_POS( pad_angle );
  705. if( aPad->m_PadShape == PAD_RECT )
  706. {
  707. wxSize size = aPad->m_Size;
  708. if( (pad_angle == 0) || (pad_angle == 900) || (pad_angle == 1800) ||
  709. (pad_angle == 2700) )
  710. {
  711. if( (pad_angle == 900) || (pad_angle == 2700) )
  712. {
  713. EXCHG( size.x, size.y );
  714. }
  715. // Test DRC:
  716. diag = false;
  717. rel_pos.x = ABS( rel_pos.x );
  718. rel_pos.y = ABS( rel_pos.y );
  719. if( ( rel_pos.x - ( (size.x + aRefPad->m_Size.x) / 2 ) ) >= dist_min )
  720. diag = true;
  721. if( ( rel_pos.y - ( (size.y + aRefPad->m_Size.y) / 2 ) ) >= dist_min )
  722. diag = true;
  723. }
  724. else // Any other orient
  725. {
  726. /* TODO : any orient ... */
  727. }
  728. }
  729. break;
  730. case PAD_OVAL: /* an oval pad is like a track segment */
  731. {
  732. /* Create and test a track segment with same dimensions */
  733. int segm_width;
  734. m_segmAngle = aRefPad->m_Orient; // Segment orient.
  735. if( aRefPad->m_Size.y < aRefPad->m_Size.x ) /* We suppose the pad is an horizontal oval */
  736. {
  737. segm_width = aRefPad->m_Size.y;
  738. m_segmLength = aRefPad->m_Size.x - aRefPad->m_Size.y;
  739. }
  740. else // it was a vertical oval, change to a rotated horizontal one
  741. {
  742. segm_width = aRefPad->m_Size.x;
  743. m_segmLength = aRefPad->m_Size.y - aRefPad->m_Size.x;
  744. m_segmAngle += 900;
  745. }
  746. /* the start point must be 0,0 and currently rel_pos is relative the center of pad coordinate */
  747. int sx = -m_segmLength / 2, sy = 0; // Start point coordinate of the horizontal equivalent segment
  748. RotatePoint( &sx, &sy, m_segmAngle ); // True start point coordinate of the equivalent segment
  749. m_spotcx = rel_pos.x + sx;
  750. m_spotcy = rel_pos.y + sy; // pad position / segment origin
  751. m_finx = -sx;
  752. m_finy = -sy; // end of segment coordinate
  753. diag = checkClearanceSegmToPad( aPad, segm_width / 2, dist_min );
  754. break;
  755. }
  756. default:
  757. /* TODO...*/
  758. break;
  759. }
  760. exit: // the only way out (hopefully) for simpler debugging
  761. return diag;
  762. }
  763. bool DRC::checkClearanceSegmToPad( const D_PAD* pad_to_test, int w_segm, int dist_min )
  764. {
  765. int p_dimx;
  766. int p_dimy; // half the dimension of the pad
  767. int orient;
  768. int x0, y0, xf, yf;
  769. int seuil;
  770. int deltay;
  771. seuil = w_segm + dist_min;
  772. p_dimx = pad_to_test->m_Size.x >> 1;
  773. p_dimy = pad_to_test->m_Size.y >> 1;
  774. if( pad_to_test->m_PadShape == PAD_CIRCLE )
  775. {
  776. /* calcul des coord centre du pad dans le repere axe X confondu
  777. * avec le segment en tst */
  778. RotatePoint( &m_spotcx, &m_spotcy, m_segmAngle );
  779. return checkMarginToCircle( m_spotcx, m_spotcy, seuil + p_dimx, m_segmLength );
  780. }
  781. else
  782. {
  783. /* calcul de la "surface de securite" du pad de reference */
  784. m_xcliplo = m_spotcx - seuil - p_dimx;
  785. m_ycliplo = m_spotcy - seuil - p_dimy;
  786. m_xcliphi = m_spotcx + seuil + p_dimx;
  787. m_ycliphi = m_spotcy + seuil + p_dimy;
  788. x0 = y0 = 0;
  789. xf = m_finx;
  790. yf = m_finy;
  791. orient = pad_to_test->m_Orient;
  792. RotatePoint( &x0, &y0, m_spotcx, m_spotcy, -orient );
  793. RotatePoint( &xf, &yf, m_spotcx, m_spotcy, -orient );
  794. if( checkLine( x0, y0, xf, yf ) )
  795. return true;
  796. /* Erreur DRC : analyse fine de la forme de la pastille */
  797. switch( pad_to_test->m_PadShape )
  798. {
  799. default:
  800. return false;
  801. case PAD_OVAL:
  802. /* test de la pastille ovale ramenee au type ovale vertical */
  803. if( p_dimx > p_dimy )
  804. {
  805. EXCHG( p_dimx, p_dimy );
  806. orient += 900;
  807. if( orient >= 3600 )
  808. orient -= 3600;
  809. }
  810. deltay = p_dimy - p_dimx;
  811. /* ici: p_dimx = rayon,
  812. * delta = dist centre cercles a centre pad */
  813. /* Test du rectangle separant les 2 demi cercles */
  814. m_xcliplo = m_spotcx - seuil - p_dimx;
  815. m_ycliplo = m_spotcy - w_segm - deltay;
  816. m_xcliphi = m_spotcx + seuil + p_dimx;
  817. m_ycliphi = m_spotcy + w_segm + deltay;
  818. if( !checkLine( x0, y0, xf, yf ) )
  819. return false;
  820. /* test des 2 cercles */
  821. x0 = m_spotcx; /* x0,y0 = centre du cercle superieur du pad ovale */
  822. y0 = m_spotcy + deltay;
  823. RotatePoint( &x0, &y0, m_spotcx, m_spotcy, orient );
  824. RotatePoint( &x0, &y0, m_segmAngle );
  825. if( !checkMarginToCircle( x0, y0, p_dimx + seuil, m_segmLength ) )
  826. return false;
  827. x0 = m_spotcx; /* x0,y0 = centre du cercle inferieur du pad ovale */
  828. y0 = m_spotcy - deltay;
  829. RotatePoint( &x0, &y0, m_spotcx, m_spotcy, orient );
  830. RotatePoint( &x0, &y0, m_segmAngle );
  831. if( !checkMarginToCircle( x0, y0, p_dimx + seuil, m_segmLength ) )
  832. return false;
  833. break;
  834. case PAD_RECT: /* 2 rectangle + 4 1/4 cercles a tester */
  835. /* Test du rectangle dimx + seuil, dimy */
  836. m_xcliplo = m_spotcx - p_dimx - seuil;
  837. m_ycliplo = m_spotcy - p_dimy;
  838. m_xcliphi = m_spotcx + p_dimx + seuil;
  839. m_ycliphi = m_spotcy + p_dimy;
  840. if( !checkLine( x0, y0, xf, yf ) )
  841. {
  842. return false;
  843. }
  844. /* Test du rectangle dimx , dimy + seuil */
  845. m_xcliplo = m_spotcx - p_dimx;
  846. m_ycliplo = m_spotcy - p_dimy - seuil;
  847. m_xcliphi = m_spotcx + p_dimx;
  848. m_ycliphi = m_spotcy + p_dimy + seuil;
  849. if( !checkLine( x0, y0, xf, yf ) )
  850. {
  851. return false;
  852. }
  853. /* test des 4 cercles ( surface d'solation autour des sommets */
  854. /* test du coin sup. gauche du pad */
  855. x0 = m_spotcx - p_dimx;
  856. y0 = m_spotcy - p_dimy;
  857. RotatePoint( &x0, &y0, m_spotcx, m_spotcy, orient );
  858. RotatePoint( &x0, &y0, m_segmAngle );
  859. if( !checkMarginToCircle( x0, y0, seuil, m_segmLength ) )
  860. {
  861. return false;
  862. }
  863. /* test du coin sup. droit du pad */
  864. x0 = m_spotcx + p_dimx;
  865. y0 = m_spotcy - p_dimy;
  866. RotatePoint( &x0, &y0, m_spotcx, m_spotcy, orient );
  867. RotatePoint( &x0, &y0, m_segmAngle );
  868. if( !checkMarginToCircle( x0, y0, seuil, m_segmLength ) )
  869. {
  870. return false;
  871. }
  872. /* test du coin inf. gauche du pad */
  873. x0 = m_spotcx - p_dimx;
  874. y0 = m_spotcy + p_dimy;
  875. RotatePoint( &x0, &y0, m_spotcx, m_spotcy, orient );
  876. RotatePoint( &x0, &y0, m_segmAngle );
  877. if( !checkMarginToCircle( x0, y0, seuil, m_segmLength ) )
  878. {
  879. return false;
  880. }
  881. /* test du coin inf. droit du pad */
  882. x0 = m_spotcx + p_dimx;
  883. y0 = m_spotcy + p_dimy;
  884. RotatePoint( &x0, &y0, m_spotcx, m_spotcy, orient );
  885. RotatePoint( &x0, &y0, m_segmAngle );
  886. if( !checkMarginToCircle( x0, y0, seuil, m_segmLength ) )
  887. {
  888. return false;
  889. }
  890. break;
  891. }
  892. }
  893. return true;
  894. }
  895. /**********************************************************************/
  896. bool DRC::checkMarginToCircle( int cx, int cy, int radius, int longueur )
  897. /**********************************************************************/
  898. {
  899. if( abs( cy ) > radius )
  900. return true;
  901. if( (cx >= -radius ) && ( cx <= (longueur + radius) ) )
  902. {
  903. if( (cx >= 0) && (cx <= longueur) )
  904. return false;
  905. if( cx > longueur )
  906. cx -= longueur;
  907. if( hypot( cx, cy ) < radius )
  908. return false;
  909. }
  910. return true;
  911. }
  912. /**********************************************/
  913. /* int Tst_Ligne(int x1,int y1,int x2,int y2) */
  914. /**********************************************/
  915. static inline int USCALE( unsigned arg, unsigned num, unsigned den )
  916. {
  917. int ii;
  918. ii = (int) ( ( (double) arg * num ) / den );
  919. return ii;
  920. }
  921. #define WHEN_OUTSIDE return true
  922. #define WHEN_INSIDE
  923. bool DRC::checkLine( int x1, int y1, int x2, int y2 )
  924. {
  925. int temp;
  926. if( x1 > x2 )
  927. {
  928. EXCHG( x1, x2 );
  929. EXCHG( y1, y2 );
  930. }
  931. if( (x2 < m_xcliplo) || (x1 > m_xcliphi) )
  932. {
  933. WHEN_OUTSIDE;
  934. }
  935. if( y1 < y2 )
  936. {
  937. if( (y2 < m_ycliplo) || (y1 > m_ycliphi) )
  938. {
  939. WHEN_OUTSIDE;
  940. }
  941. if( y1 < m_ycliplo )
  942. {
  943. temp = USCALE( (x2 - x1), (m_ycliplo - y1), (y2 - y1) );
  944. if( (x1 += temp) > m_xcliphi )
  945. {
  946. WHEN_OUTSIDE;
  947. }
  948. y1 = m_ycliplo;
  949. WHEN_INSIDE;
  950. }
  951. if( y2 > m_ycliphi )
  952. {
  953. temp = USCALE( (x2 - x1), (y2 - m_ycliphi), (y2 - y1) );
  954. if( (x2 -= temp) < m_xcliplo )
  955. {
  956. WHEN_OUTSIDE;
  957. }
  958. y2 = m_ycliphi;
  959. WHEN_INSIDE;
  960. }
  961. if( x1 < m_xcliplo )
  962. {
  963. temp = USCALE( (y2 - y1), (m_xcliplo - x1), (x2 - x1) );
  964. y1 += temp;
  965. x1 = m_xcliplo;
  966. WHEN_INSIDE;
  967. }
  968. if( x2 > m_xcliphi )
  969. {
  970. temp = USCALE( (y2 - y1), (x2 - m_xcliphi), (x2 - x1) );
  971. y2 -= temp;
  972. x2 = m_xcliphi;
  973. WHEN_INSIDE;
  974. }
  975. }
  976. else
  977. {
  978. if( (y1 < m_ycliplo) || (y2 > m_ycliphi) )
  979. {
  980. WHEN_OUTSIDE;
  981. }
  982. if( y1 > m_ycliphi )
  983. {
  984. temp = USCALE( (x2 - x1), (y1 - m_ycliphi), (y1 - y2) );
  985. if( (x1 += temp) > m_xcliphi )
  986. {
  987. WHEN_OUTSIDE;
  988. }
  989. y1 = m_ycliphi;
  990. WHEN_INSIDE;
  991. }
  992. if( y2 < m_ycliplo )
  993. {
  994. temp = USCALE( (x2 - x1), (m_ycliplo - y2), (y1 - y2) );
  995. if( (x2 -= temp) < m_xcliplo )
  996. {
  997. WHEN_OUTSIDE;
  998. }
  999. y2 = m_ycliplo;
  1000. WHEN_INSIDE;
  1001. }
  1002. if( x1 < m_xcliplo )
  1003. {
  1004. temp = USCALE( (y1 - y2), (m_xcliplo - x1), (x2 - x1) );
  1005. y1 -= temp;
  1006. x1 = m_xcliplo;
  1007. WHEN_INSIDE;
  1008. }
  1009. if( x2 > m_xcliphi )
  1010. {
  1011. temp = USCALE( (y1 - y2), (x2 - m_xcliphi), (x2 - x1) );
  1012. y2 += temp;
  1013. x2 = m_xcliphi;
  1014. WHEN_INSIDE;
  1015. }
  1016. }
  1017. if( ( (x2 + x1)/2 <= m_xcliphi ) && ( (x2 + x1)/2 >= m_xcliplo ) \
  1018. && ( (y2 + y1)/2 <= m_ycliphi ) && ( (y2 + y1)/2 >= m_ycliplo ) )
  1019. {
  1020. return false;
  1021. }
  1022. else
  1023. return true;
  1024. }