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.

732 lines
28 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-2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2007 Dick Hollenbeck, dick@softplc.com
  6. * Copyright (C) 2019 KiCad Developers, see AUTHORS.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. #include <fctsys.h>
  26. #include <trigo.h>
  27. #include <drc/drc.h>
  28. #include <class_board.h>
  29. #include <class_track.h>
  30. #include <class_drawsegment.h>
  31. #include <class_marker_pcb.h>
  32. #include <math_for_graphics.h>
  33. #include <geometry/polygon_test_point_inside.h>
  34. #include <convert_basic_shapes_to_polygon.h>
  35. #include <geometry/shape_rect.h>
  36. /*
  37. * compare a trapezoid (can be rectangle) and a segment and return true if distance > aDist
  38. */
  39. bool poly2segmentDRC( wxPoint* aTref, int aTrefCount, wxPoint aSegStart, wxPoint aSegEnd,
  40. int aDist, int* aActual )
  41. {
  42. /* Test if the segment is contained in the polygon.
  43. * This case is not covered by the following check if the segment is
  44. * completely contained in the polygon (because edges don't intersect)!
  45. */
  46. if( TestPointInsidePolygon( aTref, aTrefCount, aSegStart ) )
  47. {
  48. *aActual = 0;
  49. return false;
  50. }
  51. for( int ii = 0, jj = aTrefCount-1; ii < aTrefCount; jj = ii, ii++ )
  52. { // for all edges in polygon
  53. double d;
  54. if( TestForIntersectionOfStraightLineSegments( aTref[ii].x, aTref[ii].y, aTref[jj].x,
  55. aTref[jj].y, aSegStart.x, aSegStart.y,
  56. aSegEnd.x, aSegEnd.y, NULL, NULL, &d ) )
  57. {
  58. *aActual = 0;
  59. return false;
  60. }
  61. if( d < aDist )
  62. {
  63. *aActual = KiROUND( d );
  64. return false;
  65. }
  66. }
  67. return true;
  68. }
  69. void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aStartIt,
  70. TRACKS::iterator aEndIt, bool aTestZones )
  71. {
  72. BOARD_DESIGN_SETTINGS& bds = m_pcb->GetDesignSettings();
  73. SEG refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
  74. PCB_LAYER_ID refLayer = aRefSeg->GetLayer();
  75. LSET refLayerSet = aRefSeg->GetLayerSet();
  76. EDA_RECT refSegBB = aRefSeg->GetBoundingBox();
  77. int refSegWidth = aRefSeg->GetWidth();
  78. /******************************************/
  79. /* Phase 0 : via DRC tests : */
  80. /******************************************/
  81. if( aRefSeg->Type() == PCB_VIA_T )
  82. {
  83. VIA *refvia = static_cast<VIA*>( aRefSeg );
  84. int viaAnnulus = ( refvia->GetWidth() - refvia->GetDrill() ) / 2;
  85. int minAnnulus = refvia->GetMinAnnulus( &m_clearanceSource );
  86. // test if the via size is smaller than minimum
  87. if( refvia->GetViaType() == VIATYPE::MICROVIA )
  88. {
  89. if( viaAnnulus < minAnnulus )
  90. {
  91. DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_VIA_ANNULUS );
  92. m_msg.Printf( _( "Via annulus too small (%s %s; actual %s)" ),
  93. m_clearanceSource,
  94. MessageTextFromValue( userUnits(), minAnnulus, true ),
  95. MessageTextFromValue( userUnits(), viaAnnulus, true ) );
  96. drcItem->SetErrorMessage( m_msg );
  97. drcItem->SetItems( refvia );
  98. MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
  99. addMarkerToPcb( aCommit, marker );
  100. }
  101. if( refvia->GetWidth() < bds.m_MicroViasMinSize )
  102. {
  103. DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_TOO_SMALL_MICROVIA );
  104. m_msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ),
  105. MessageTextFromValue( userUnits(), bds.m_MicroViasMinSize, true ),
  106. MessageTextFromValue( userUnits(), refvia->GetWidth(), true ) );
  107. drcItem->SetErrorMessage( m_msg );
  108. drcItem->SetItems( refvia );
  109. MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
  110. addMarkerToPcb( aCommit, marker );
  111. }
  112. }
  113. else
  114. {
  115. if( bds.m_ViasMinAnnulus > minAnnulus )
  116. {
  117. minAnnulus = bds.m_ViasMinAnnulus;
  118. m_clearanceSource = _( "board minimum" );
  119. }
  120. if( viaAnnulus < minAnnulus )
  121. {
  122. DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_VIA_ANNULUS );
  123. m_msg.Printf( _( "Via annulus too small (%s %s; actual %s)" ),
  124. m_clearanceSource,
  125. MessageTextFromValue( userUnits(), minAnnulus, true ),
  126. MessageTextFromValue( userUnits(), viaAnnulus, true ) );
  127. drcItem->SetErrorMessage( m_msg );
  128. drcItem->SetItems( refvia );
  129. MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
  130. addMarkerToPcb( aCommit, marker );
  131. }
  132. if( refvia->GetWidth() < bds.m_ViasMinSize )
  133. {
  134. DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_TOO_SMALL_VIA );
  135. m_msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ),
  136. MessageTextFromValue( userUnits(), bds.m_ViasMinSize, true ),
  137. MessageTextFromValue( userUnits(), refvia->GetWidth(), true ) );
  138. drcItem->SetErrorMessage( m_msg );
  139. drcItem->SetItems( refvia );
  140. MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
  141. addMarkerToPcb( aCommit, marker );
  142. }
  143. }
  144. // test if via's hole is bigger than its diameter
  145. // This test is necessary since the via hole size and width can be modified
  146. // and a default via hole can be bigger than some vias sizes
  147. if( refvia->GetDrillValue() > refvia->GetWidth() )
  148. {
  149. DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_VIA_HOLE_BIGGER );
  150. m_msg.Printf( drcItem->GetErrorText() + _( " (diameter %s; drill %s)" ),
  151. MessageTextFromValue( userUnits(), refvia->GetWidth(), true ),
  152. MessageTextFromValue( userUnits(), refvia->GetDrillValue(), true ) );
  153. drcItem->SetErrorMessage( m_msg );
  154. drcItem->SetItems( refvia );
  155. MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
  156. addMarkerToPcb( aCommit, marker );
  157. }
  158. // test if the type of via is allowed due to design rules
  159. if( refvia->GetViaType() == VIATYPE::MICROVIA && !bds.m_MicroViasAllowed )
  160. {
  161. DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_ALLOWED_ITEMS );
  162. m_msg.Printf( _( "Microvia not allowed (board design rule constraints)" ) );
  163. drcItem->SetErrorMessage( m_msg );
  164. drcItem->SetItems( refvia );
  165. MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
  166. addMarkerToPcb( aCommit, marker );
  167. }
  168. // test if the type of via is allowed due to design rules
  169. if( refvia->GetViaType() == VIATYPE::BLIND_BURIED && !bds.m_BlindBuriedViaAllowed )
  170. {
  171. DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_ALLOWED_ITEMS );
  172. m_msg.Printf( _( "Blind/buried via not allowed (board design rule constraints)" ) );
  173. drcItem->SetErrorMessage( m_msg );
  174. drcItem->SetItems( refvia );
  175. MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
  176. addMarkerToPcb( aCommit, marker );
  177. }
  178. // For microvias: test if they are blind vias and only between 2 layers
  179. // because they are used for very small drill size and are drill by laser
  180. // and **only one layer** can be drilled
  181. if( refvia->GetViaType() == VIATYPE::MICROVIA )
  182. {
  183. PCB_LAYER_ID layer1, layer2;
  184. bool err = true;
  185. refvia->LayerPair( &layer1, &layer2 );
  186. if( layer1 > layer2 )
  187. std::swap( layer1, layer2 );
  188. if( layer2 == B_Cu && layer1 == bds.GetCopperLayerCount() - 2 )
  189. err = false;
  190. else if( layer1 == F_Cu && layer2 == In1_Cu )
  191. err = false;
  192. if( err )
  193. {
  194. DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_PADSTACK );
  195. m_msg.Printf( _( "Microvia through too many layers (%s and %s not adjacent)" ),
  196. m_pcb->GetLayerName( layer1 ),
  197. m_pcb->GetLayerName( layer2 ) );
  198. drcItem->SetErrorMessage( m_msg );
  199. drcItem->SetItems( refvia );
  200. MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
  201. addMarkerToPcb( aCommit, marker );
  202. }
  203. }
  204. }
  205. else // This is a track segment
  206. {
  207. int minWidth, maxWidth;
  208. aRefSeg->GetWidthConstraints( &minWidth, &maxWidth, &m_clearanceSource );
  209. int errorCode = 0;
  210. int constraintWidth;
  211. if( refSegWidth < minWidth )
  212. {
  213. errorCode = DRCE_TRACK_WIDTH;
  214. constraintWidth = minWidth;
  215. }
  216. else if( refSegWidth > maxWidth )
  217. {
  218. errorCode = DRCE_TRACK_WIDTH;
  219. constraintWidth = maxWidth;
  220. }
  221. if( errorCode )
  222. {
  223. wxPoint refsegMiddle = ( aRefSeg->GetStart() + aRefSeg->GetEnd() ) / 2;
  224. DRC_ITEM* drcItem = DRC_ITEM::Create( errorCode );
  225. m_msg.Printf( drcItem->GetErrorText() + _( " (%s %s; actual %s)" ),
  226. m_clearanceSource,
  227. MessageTextFromValue( userUnits(), constraintWidth, true ),
  228. MessageTextFromValue( userUnits(), refSegWidth, true ) );
  229. drcItem->SetErrorMessage( m_msg );
  230. drcItem->SetItems( aRefSeg );
  231. MARKER_PCB* marker = new MARKER_PCB( drcItem, refsegMiddle );
  232. addMarkerToPcb( aCommit, marker );
  233. }
  234. }
  235. /******************************************/
  236. /* Phase 1 : test DRC track to pads : */
  237. /******************************************/
  238. // Compute the min distance to pads
  239. for( MODULE* mod : m_pcb->Modules() )
  240. {
  241. // Don't preflight at the module level. Getting a module's bounding box goes
  242. // through all its pads anyway (so it's no faster), and also all its drawings
  243. // (so it's in fact slower).
  244. for( D_PAD* pad : mod->Pads() )
  245. {
  246. // Preflight based on bounding boxes.
  247. EDA_RECT inflatedBB = refSegBB;
  248. inflatedBB.Inflate( pad->GetBoundingRadius() + m_largestClearance );
  249. if( !inflatedBB.Contains( pad->GetPosition() ) )
  250. continue;
  251. if( !( pad->GetLayerSet() & refLayerSet ).any() )
  252. continue;
  253. // No need to check pads with the same net as the refSeg.
  254. if( pad->GetNetCode() && aRefSeg->GetNetCode() == pad->GetNetCode() )
  255. continue;
  256. if( pad->GetDrillSize().x > 0 )
  257. {
  258. // For hole testing we use a dummy pad which is a copy of the current pad
  259. // shrunk down to nothing but its hole.
  260. D_PAD dummypad( *pad );
  261. dummypad.SetSize( pad->GetDrillSize() );
  262. dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ?
  263. PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE );
  264. // Ensure the hole is on all copper layers
  265. const static LSET all_cu = LSET::AllCuMask();
  266. dummypad.SetLayerSet( all_cu | dummypad.GetLayerSet() );
  267. int minClearance;
  268. DRC_RULE* rule = GetRule( aRefSeg, &dummypad, CLEARANCE_CONSTRAINT );
  269. if( rule )
  270. {
  271. m_clearanceSource = wxString::Format( _( "'%s' rule" ), rule->m_Name );
  272. minClearance = rule->m_Clearance.Min;
  273. }
  274. else
  275. {
  276. minClearance = aRefSeg->GetClearance( nullptr, &m_clearanceSource );
  277. }
  278. /* Treat an oval hole as a line segment along the hole's major axis,
  279. * shortened by half its minor axis.
  280. * A circular hole is just a degenerate case of an oval hole.
  281. */
  282. wxPoint slotStart, slotEnd;
  283. int slotWidth;
  284. pad->GetOblongGeometry( pad->GetDrillSize(), &slotStart, &slotEnd, &slotWidth );
  285. slotStart += pad->GetPosition();
  286. slotEnd += pad->GetPosition();
  287. SEG slotSeg( slotStart, slotEnd );
  288. int widths = ( slotWidth + refSegWidth ) / 2;
  289. int center2centerAllowed = minClearance + widths + bds.GetDRCEpsilon();
  290. // Avoid square-roots if possible (for performance)
  291. SEG::ecoord center2center_squared = refSeg.SquaredDistance( slotSeg );
  292. if( center2center_squared < SEG::Square( center2centerAllowed ) )
  293. {
  294. int actual = std::max( 0.0, sqrt( center2center_squared ) - widths );
  295. DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
  296. m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
  297. m_clearanceSource,
  298. MessageTextFromValue( userUnits(), minClearance, true ),
  299. MessageTextFromValue( userUnits(), actual, true ) );
  300. drcItem->SetErrorMessage( m_msg );
  301. drcItem->SetItems( aRefSeg, pad );
  302. MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aRefSeg, slotSeg ) );
  303. addMarkerToPcb( aCommit, marker );
  304. if( !m_reportAllTrackErrors )
  305. return;
  306. }
  307. }
  308. int minClearance = aRefSeg->GetClearance( pad, &m_clearanceSource );
  309. int clearanceAllowed = minClearance - bds.GetDRCEpsilon();
  310. int actual;
  311. if( !checkClearanceSegmToPad( refSeg, refSegWidth, pad, clearanceAllowed, &actual ) )
  312. {
  313. actual = std::max( 0, actual );
  314. SEG padSeg( pad->GetPosition(), pad->GetPosition() );
  315. DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
  316. m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
  317. m_clearanceSource,
  318. MessageTextFromValue( userUnits(), minClearance, true ),
  319. MessageTextFromValue( userUnits(), actual, true ) );
  320. drcItem->SetErrorMessage( m_msg );
  321. drcItem->SetItems( aRefSeg, pad );
  322. MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aRefSeg, padSeg ) );
  323. addMarkerToPcb( aCommit, marker );
  324. if( !m_reportAllTrackErrors )
  325. return;
  326. }
  327. }
  328. }
  329. /***********************************************/
  330. /* Phase 2: test DRC with other track segments */
  331. /***********************************************/
  332. // Test the reference segment with other track segments
  333. for( auto it = aStartIt; it != aEndIt; it++ )
  334. {
  335. TRACK* track = *it;
  336. // No problem if segments have the same net code:
  337. if( aRefSeg->GetNetCode() == track->GetNetCode() )
  338. continue;
  339. // No problem if tracks are on different layers:
  340. // Note that while the general case of GetLayerSet intersection always works,
  341. // the others are much faster.
  342. bool sameLayers;
  343. if( aRefSeg->Type() == PCB_VIA_T )
  344. {
  345. if( track->Type() == PCB_VIA_T )
  346. sameLayers = ( refLayerSet & track->GetLayerSet() ).any();
  347. else
  348. sameLayers = refLayerSet.test( track->GetLayer() );
  349. }
  350. else
  351. {
  352. if( track->Type() == PCB_VIA_T )
  353. sameLayers = track->GetLayerSet().test( refLayer );
  354. else
  355. sameLayers = track->GetLayer() == refLayer;
  356. }
  357. if( !sameLayers )
  358. continue;
  359. // Preflight based on worst-case inflated bounding boxes:
  360. EDA_RECT trackBB = track->GetBoundingBox();
  361. trackBB.Inflate( m_largestClearance );
  362. if( !trackBB.Intersects( refSegBB ) )
  363. continue;
  364. int minClearance = aRefSeg->GetClearance( track, &m_clearanceSource );
  365. SEG trackSeg( track->GetStart(), track->GetEnd() );
  366. int widths = ( refSegWidth + track->GetWidth() ) / 2;
  367. int center2centerAllowed = minClearance + widths;
  368. // Avoid square-roots if possible (for performance)
  369. SEG::ecoord center2center_squared = refSeg.SquaredDistance( trackSeg );
  370. OPT_VECTOR2I intersection = refSeg.Intersect( trackSeg );
  371. // Check two tracks crossing first as it reports a DRCE without distances
  372. if( intersection )
  373. {
  374. DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_TRACKS_CROSSING );
  375. drcItem->SetErrorMessage( m_msg );
  376. drcItem->SetItems( aRefSeg, track );
  377. MARKER_PCB* marker = new MARKER_PCB( drcItem, (wxPoint) intersection.get() );
  378. addMarkerToPcb( aCommit, marker );
  379. if( !m_reportAllTrackErrors )
  380. return;
  381. }
  382. else if( center2center_squared < SEG::Square( center2centerAllowed ) )
  383. {
  384. int actual = std::max( 0.0, sqrt( center2center_squared ) - widths );
  385. DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
  386. m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
  387. m_clearanceSource,
  388. MessageTextFromValue( userUnits(), minClearance, true ),
  389. MessageTextFromValue( userUnits(), actual, true ) );
  390. drcItem->SetErrorMessage( m_msg );
  391. drcItem->SetItems( aRefSeg, track );
  392. MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aRefSeg, trackSeg ) );
  393. addMarkerToPcb( aCommit, marker );
  394. if( !m_reportAllTrackErrors )
  395. return;
  396. }
  397. }
  398. /***************************************/
  399. /* Phase 3: test DRC with copper zones */
  400. /***************************************/
  401. // Can be *very* time consumming.
  402. if( aTestZones )
  403. {
  404. SEG testSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
  405. for( ZONE_CONTAINER* zone : m_pcb->Zones() )
  406. {
  407. if( !( refLayerSet & zone->GetLayerSet() ).any() || zone->GetIsKeepout() )
  408. continue;
  409. for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
  410. {
  411. if( zone->GetFilledPolysList( layer ).IsEmpty() )
  412. continue;
  413. if( zone->GetNetCode() && zone->GetNetCode() == aRefSeg->GetNetCode() )
  414. continue;
  415. int minClearance = aRefSeg->GetClearance( zone, &m_clearanceSource );
  416. int widths = refSegWidth / 2;
  417. int center2centerAllowed = minClearance + widths;
  418. SHAPE_POLY_SET* outline =
  419. const_cast<SHAPE_POLY_SET*>( &zone->GetFilledPolysList( layer ) );
  420. SEG::ecoord center2center_squared = outline->SquaredDistance( testSeg );
  421. // to avoid false positive, due to rounding issues and approxiamtions
  422. // in distance and clearance calculations, use a small threshold for distance
  423. // (1 micron)
  424. #define THRESHOLD_DIST Millimeter2iu( 0.001 )
  425. if( center2center_squared + THRESHOLD_DIST < SEG::Square( center2centerAllowed ) )
  426. {
  427. int actual = std::max( 0.0, sqrt( center2center_squared ) - widths );
  428. DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
  429. m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
  430. m_clearanceSource,
  431. MessageTextFromValue( userUnits(), minClearance, true ),
  432. MessageTextFromValue( userUnits(), actual, true ) );
  433. drcItem->SetErrorMessage( m_msg );
  434. drcItem->SetItems( aRefSeg, zone );
  435. MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aRefSeg, zone ) );
  436. addMarkerToPcb( aCommit, marker );
  437. }
  438. }
  439. }
  440. }
  441. /***********************************************/
  442. /* Phase 4: test DRC with to board edge */
  443. /***********************************************/
  444. if( m_board_outline_valid )
  445. {
  446. int minClearance = bds.m_CopperEdgeClearance;
  447. m_clearanceSource = _( "board edge" );
  448. static DRAWSEGMENT dummyEdge;
  449. dummyEdge.SetLayer( Edge_Cuts );
  450. if( aRefSeg->GetRuleClearance( &dummyEdge, &minClearance, &m_clearanceSource ) )
  451. /* minClearance and m_clearanceSource set in GetRuleClearance() */;
  452. SEG testSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
  453. int halfWidth = refSegWidth / 2;
  454. int center2centerAllowed = minClearance + halfWidth;
  455. for( auto it = m_board_outlines.IterateSegmentsWithHoles(); it; it++ )
  456. {
  457. SEG::ecoord center2center_squared = testSeg.SquaredDistance( *it );
  458. if( center2center_squared < SEG::Square( center2centerAllowed ) )
  459. {
  460. VECTOR2I pt = testSeg.NearestPoint( *it );
  461. KICAD_T types[] = { PCB_LINE_T, EOT };
  462. DRAWSEGMENT* edge = nullptr;
  463. INSPECTOR_FUNC inspector =
  464. [&] ( EDA_ITEM* item, void* testData )
  465. {
  466. DRAWSEGMENT* test_edge = dynamic_cast<DRAWSEGMENT*>( item );
  467. if( !test_edge || test_edge->GetLayer() != Edge_Cuts )
  468. return SEARCH_RESULT::CONTINUE;
  469. if( test_edge->HitTest( (wxPoint) pt, minClearance + halfWidth ) )
  470. {
  471. edge = test_edge;
  472. return SEARCH_RESULT::QUIT;
  473. }
  474. return SEARCH_RESULT::CONTINUE;
  475. };
  476. // Best-efforts search for edge segment
  477. BOARD::IterateForward<BOARD_ITEM*>( m_pcb->Drawings(), inspector, nullptr, types );
  478. int actual = std::max( 0.0, sqrt( center2center_squared ) - halfWidth );
  479. DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_COPPER_EDGE_CLEARANCE );
  480. m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
  481. m_clearanceSource,
  482. MessageTextFromValue( userUnits(), minClearance, true ),
  483. MessageTextFromValue( userUnits(), actual, true ) );
  484. drcItem->SetErrorMessage( m_msg );
  485. drcItem->SetItems( aRefSeg, edge );
  486. MARKER_PCB* marker = new MARKER_PCB( drcItem, (wxPoint) pt );
  487. addMarkerToPcb( aCommit, marker );
  488. }
  489. }
  490. }
  491. }
  492. bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int aMinClearance, int* aActual )
  493. {
  494. int center2center = KiROUND( EuclideanNorm( aPad->ShapePos() - aRefPad->ShapePos() ) );
  495. // Quick test: Clearance is OK if the bounding circles are further away than aMinClearance
  496. if( center2center - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius() >= aMinClearance )
  497. return true;
  498. int actual = INT_MAX;
  499. for( const std::shared_ptr<SHAPE>& aShape : aRefPad->GetEffectiveShapes() )
  500. {
  501. for( const std::shared_ptr<SHAPE>& bShape : aPad->GetEffectiveShapes() )
  502. {
  503. int this_dist;
  504. if( aShape->Collide( bShape.get(), aMinClearance, &this_dist ) )
  505. actual = std::min( actual, this_dist );
  506. }
  507. }
  508. if( actual < INT_MAX )
  509. {
  510. // returns the actual clearance (clearance < aMinClearance) for diags:
  511. if( aActual )
  512. *aActual = std::max( 0, actual );
  513. return false;
  514. }
  515. return true;
  516. }
  517. /*
  518. * Test if distance between a segment and a pad is > minClearance. Return the actual
  519. * distance if it is less.
  520. */
  521. bool DRC::checkClearanceSegmToPad( const SEG& refSeg, int refSegWidth, const D_PAD* pad,
  522. int minClearance, int* aActualDist )
  523. {
  524. if( ( pad->GetShape() == PAD_SHAPE_CIRCLE || pad->GetShape() == PAD_SHAPE_OVAL ) )
  525. {
  526. /* Treat an oval pad as a line segment along the hole's major axis,
  527. * shortened by half its minor axis.
  528. * A circular pad is just a degenerate case of an oval hole.
  529. */
  530. wxPoint padStart, padEnd;
  531. int padWidth;
  532. pad->GetOblongGeometry( pad->GetSize(), &padStart, &padEnd, &padWidth );
  533. padStart += pad->ShapePos();
  534. padEnd += pad->ShapePos();
  535. SEG padSeg( padStart, padEnd );
  536. int widths = ( padWidth + refSegWidth ) / 2;
  537. int center2centerAllowed = minClearance + widths;
  538. // Avoid square-roots if possible (for performance)
  539. SEG::ecoord center2center_squared = refSeg.SquaredDistance( padSeg );
  540. if( center2center_squared < SEG::Square( center2centerAllowed ) )
  541. {
  542. *aActualDist = std::max( 0.0, sqrt( center2center_squared ) - widths );
  543. return false;
  544. }
  545. }
  546. else if( ( pad->GetShape() == PAD_SHAPE_RECT || pad->GetShape() == PAD_SHAPE_ROUNDRECT )
  547. && ( (int) pad->GetOrientation() % 900 == 0 ) )
  548. {
  549. EDA_RECT padBBox = pad->GetBoundingBox();
  550. int widths = refSegWidth / 2;
  551. // Note a ROUNDRECT pad with a corner radius = r can be treated as a smaller
  552. // RECT (size - 2*r) with a clearance increased by r
  553. if( pad->GetShape() == PAD_SHAPE_ROUNDRECT )
  554. {
  555. padBBox.Inflate( - pad->GetRoundRectCornerRadius() );
  556. widths += pad->GetRoundRectCornerRadius();
  557. }
  558. SHAPE_RECT padShape( padBBox.GetPosition(), padBBox.GetWidth(), padBBox.GetHeight() );
  559. int actual;
  560. if( padShape.Collide( refSeg, minClearance + widths, &actual ) )
  561. {
  562. *aActualDist = std::max( 0, actual - widths );
  563. return false;
  564. }
  565. }
  566. else // Convert the rest to polygons
  567. {
  568. SHAPE_POLY_SET polyset;
  569. BOARD* board = pad->GetBoard();
  570. int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF;
  571. pad->TransformShapeWithClearanceToPolygon( polyset, 0, maxError );
  572. const SHAPE_LINE_CHAIN& refpoly = polyset.COutline( 0 );
  573. int widths = refSegWidth / 2;
  574. int actual;
  575. if( !poly2segmentDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
  576. (wxPoint) refSeg.A, (wxPoint) refSeg.B,
  577. minClearance + widths, &actual ) )
  578. {
  579. *aActualDist = std::max( 0, actual - widths );
  580. return false;
  581. }
  582. }
  583. return true;
  584. }