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.

1583 lines
56 KiB

14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
* KIWAY Milestone A): Make major modules into DLL/DSOs. ! The initial testing of this commit should be done using a Debug build so that all the wxASSERT()s are enabled. Also, be sure and keep enabled the USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it off is senseless anyways. If you want stable code, go back to a prior version, the one tagged with "stable". * Relocate all functionality out of the wxApp derivative into more finely targeted purposes: a) DLL/DSO specific b) PROJECT specific c) EXE or process specific d) configuration file specific data e) configuration file manipulations functions. All of this functionality was blended into an extremely large wxApp derivative and that was incompatible with the desire to support multiple concurrently loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects. An amazing amount of organization come from simply sorting each bit of functionality into the proper box. * Switch to wxConfigBase from wxConfig everywhere except instantiation. * Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD, PGM_SINGLE_TOP, * Remove "Return" prefix on many function names. * Remove obvious comments from CMakeLists.txt files, and from else() and endif()s. * Fix building boost for use in a DSO on linux. * Remove some of the assumptions in the CMakeLists.txt files that windows had to be the host platform when building windows binaries. * Reduce the number of wxStrings being constructed at program load time via static construction. * Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that these functions are useful even when the wxConfigBase comes from another source, as is the case in the KICAD_MANAGER_FRAME. * Move the setting of the KIPRJMOD environment variable into class PROJECT, so that it can be moved into a project variable soon, and out of FP_LIB_TABLE. * Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all its child wxFrames and wxDialogs now have a Kiway() member function which returns a KIWAY& that that window tree branch is in support of. This is like wxWindows DNA in that child windows get this member with proper value at time of construction. * Anticipate some of the needs for milestones B) and C) and make code adjustments now in an effort to reduce work in those milestones. * No testing has been done for python scripting, since milestone C) has that being largely reworked and re-thought-out.
12 years ago
* KIWAY Milestone A): Make major modules into DLL/DSOs. ! The initial testing of this commit should be done using a Debug build so that all the wxASSERT()s are enabled. Also, be sure and keep enabled the USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it off is senseless anyways. If you want stable code, go back to a prior version, the one tagged with "stable". * Relocate all functionality out of the wxApp derivative into more finely targeted purposes: a) DLL/DSO specific b) PROJECT specific c) EXE or process specific d) configuration file specific data e) configuration file manipulations functions. All of this functionality was blended into an extremely large wxApp derivative and that was incompatible with the desire to support multiple concurrently loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects. An amazing amount of organization come from simply sorting each bit of functionality into the proper box. * Switch to wxConfigBase from wxConfig everywhere except instantiation. * Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD, PGM_SINGLE_TOP, * Remove "Return" prefix on many function names. * Remove obvious comments from CMakeLists.txt files, and from else() and endif()s. * Fix building boost for use in a DSO on linux. * Remove some of the assumptions in the CMakeLists.txt files that windows had to be the host platform when building windows binaries. * Reduce the number of wxStrings being constructed at program load time via static construction. * Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that these functions are useful even when the wxConfigBase comes from another source, as is the case in the KICAD_MANAGER_FRAME. * Move the setting of the KIPRJMOD environment variable into class PROJECT, so that it can be moved into a project variable soon, and out of FP_LIB_TABLE. * Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all its child wxFrames and wxDialogs now have a Kiway() member function which returns a KIWAY& that that window tree branch is in support of. This is like wxWindows DNA in that child windows get this member with proper value at time of construction. * Anticipate some of the needs for milestones B) and C) and make code adjustments now in an effort to reduce work in those milestones. * No testing has been done for python scripting, since milestone C) has that being largely reworked and re-thought-out.
12 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
  1. /**
  2. * @file drc_clearance_test_functions.cpp
  3. */
  4. /*
  5. * This program source code file is part of KiCad, a free EDA CAD application.
  6. *
  7. * Copyright (C) 2004-2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
  8. * Copyright (C) 2007 Dick Hollenbeck, dick@softplc.com
  9. * Copyright (C) 2018 KiCad Developers, see AUTHORS.txt for contributors.
  10. *
  11. * This program is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU General Public License
  13. * as published by the Free Software Foundation; either version 2
  14. * of the License, or (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, you may find one here:
  23. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  24. * or you may search the http://www.gnu.org website for the version 2 license,
  25. * or you may write to the Free Software Foundation, Inc.,
  26. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  27. */
  28. /**
  29. * DRC control: these functions make a DRC between pads, tracks and pads versus tracks
  30. */
  31. #include <fctsys.h>
  32. #include <pcb_edit_frame.h>
  33. #include <trigo.h>
  34. #include <pcbnew.h>
  35. #include <drc.h>
  36. #include <class_board.h>
  37. #include <class_module.h>
  38. #include <class_track.h>
  39. #include <class_zone.h>
  40. #include <class_marker_pcb.h>
  41. #include <math_for_graphics.h>
  42. #include <polygon_test_point_inside.h>
  43. #include <convert_basic_shapes_to_polygon.h>
  44. #include <board_commit.h>
  45. /* compare 2 convex polygons and return true if distance > aDist
  46. * i.e if for each edge of the first polygon distance from each edge of the other polygon
  47. * is >= aDist
  48. */
  49. bool poly2polyDRC( wxPoint* aTref, int aTrefCount,
  50. wxPoint* aTcompare, int aTcompareCount, int aDist )
  51. {
  52. /* Test if one polygon is contained in the other and thus the polygon overlap.
  53. * This case is not covered by the following check if one polygone is
  54. * completely contained in the other (because edges don't intersect)!
  55. */
  56. if( TestPointInsidePolygon( aTref, aTrefCount, aTcompare[0] ) )
  57. return false;
  58. if( TestPointInsidePolygon( aTcompare, aTcompareCount, aTref[0] ) )
  59. return false;
  60. for( int ii = 0, jj = aTrefCount - 1; ii < aTrefCount; jj = ii, ii++ )
  61. { // for all edges in aTref
  62. for( int kk = 0, ll = aTcompareCount - 1; kk < aTcompareCount; ll = kk, kk++ )
  63. { // for all edges in aTcompare
  64. double d;
  65. int intersect = TestForIntersectionOfStraightLineSegments(
  66. aTref[ii].x, aTref[ii].y, aTref[jj].x, aTref[jj].y,
  67. aTcompare[kk].x, aTcompare[kk].y, aTcompare[ll].x, aTcompare[ll].y,
  68. NULL, NULL, &d );
  69. if( intersect || ( d < aDist ) )
  70. return false;
  71. }
  72. }
  73. return true;
  74. }
  75. /* compare a trapezoids (can be rectangle) and a segment and return true if distance > aDist
  76. */
  77. bool poly2segmentDRC( wxPoint* aTref, int aTrefCount, wxPoint aSegStart, wxPoint aSegEnd, int aDist )
  78. {
  79. /* Test if the segment is contained in the polygon.
  80. * This case is not covered by the following check if the segment is
  81. * completely contained in the polygon (because edges don't intersect)!
  82. */
  83. if( TestPointInsidePolygon( aTref, aTrefCount, aSegStart ) )
  84. return false;
  85. for( int ii = 0, jj = aTrefCount-1; ii < aTrefCount; jj = ii, ii++ )
  86. { // for all edges in polygon
  87. double d;
  88. int intersect = TestForIntersectionOfStraightLineSegments(
  89. aTref[ii].x, aTref[ii].y, aTref[jj].x, aTref[jj].y,
  90. aSegStart.x, aSegStart.y, aSegEnd.x, aSegEnd.y,
  91. NULL, NULL, &d );
  92. if( intersect || ( d < aDist) )
  93. return false;
  94. }
  95. return true;
  96. }
  97. /* compare a polygon to a point and return true if distance > aDist
  98. * do not use this function for horizontal or vertical rectangles
  99. * because there is a faster an easier way to compare the distance
  100. */
  101. bool convex2pointDRC( wxPoint* aTref, int aTrefCount, wxPoint aPcompare, int aDist )
  102. {
  103. /* Test if aPcompare point is contained in the polygon.
  104. * This case is not covered by the following check if this point is inside the polygon
  105. */
  106. if( TestPointInsidePolygon( aTref, aTrefCount, aPcompare ) )
  107. {
  108. return false;
  109. }
  110. // Test distance between aPcompare and each segment of the polygon:
  111. for( int ii = 0, jj = aTrefCount - 1; ii < aTrefCount; jj = ii, ii++ ) // for all edge in polygon
  112. {
  113. if( TestSegmentHit( aPcompare, aTref[ii], aTref[jj], aDist ) )
  114. return false;
  115. }
  116. return true;
  117. }
  118. bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
  119. {
  120. TRACK* track;
  121. wxPoint delta; // length on X and Y axis of segments
  122. LSET layerMask;
  123. int net_code_ref;
  124. wxPoint shape_pos;
  125. std::vector<MARKER_PCB*> markers;
  126. auto commitMarkers = [&]()
  127. {
  128. // In legacy routing mode, do not add markers to the board.
  129. // only shows the drc error message
  130. if( m_drcInLegacyRoutingMode )
  131. {
  132. while( markers.size() > 0 )
  133. {
  134. m_pcbEditorFrame->SetMsgPanel( markers.back() );
  135. delete markers.back();
  136. markers.pop_back();
  137. }
  138. }
  139. else
  140. {
  141. BOARD_COMMIT commit( m_pcbEditorFrame );
  142. for( auto marker : markers )
  143. commit.Add( marker );
  144. commit.Push( wxEmptyString, false, false );
  145. }
  146. };
  147. // Returns false if we should return false from call site, or true to continue
  148. auto handleNewMarker = [&]() -> bool
  149. {
  150. if( !m_reportAllTrackErrors )
  151. {
  152. if( markers.size() > 0 )
  153. commitMarkers();
  154. return false;
  155. }
  156. else
  157. return true;
  158. };
  159. NETCLASSPTR netclass = aRefSeg->GetNetClass();
  160. BOARD_DESIGN_SETTINGS& dsnSettings = m_pcb->GetDesignSettings();
  161. /* In order to make some calculations more easier or faster,
  162. * pads and tracks coordinates will be made relative to the reference segment origin
  163. */
  164. wxPoint origin = aRefSeg->GetStart(); // origin will be the origin of other coordinates
  165. m_segmEnd = delta = aRefSeg->GetEnd() - origin;
  166. m_segmAngle = 0;
  167. layerMask = aRefSeg->GetLayerSet();
  168. net_code_ref = aRefSeg->GetNetCode();
  169. /******************************************/
  170. /* Phase 0 : via DRC tests : */
  171. /******************************************/
  172. if( aRefSeg->Type() == PCB_VIA_T )
  173. {
  174. VIA *refvia = static_cast<VIA*>( aRefSeg );
  175. wxPoint refviaPos = refvia->GetPosition();
  176. // test if the via size is smaller than minimum
  177. if( refvia->GetViaType() == VIA_MICROVIA )
  178. {
  179. if( refvia->GetWidth() < dsnSettings.m_MicroViasMinSize )
  180. {
  181. markers.push_back(
  182. m_markerFactory.NewMarker( refviaPos, refvia, DRCE_TOO_SMALL_MICROVIA ) );
  183. if( !handleNewMarker() )
  184. return false;
  185. }
  186. if( refvia->GetDrillValue() < dsnSettings.m_MicroViasMinDrill )
  187. {
  188. markers.push_back( m_markerFactory.NewMarker(
  189. refviaPos, refvia, DRCE_TOO_SMALL_MICROVIA_DRILL ) );
  190. if( !handleNewMarker() )
  191. return false;
  192. }
  193. }
  194. else
  195. {
  196. if( refvia->GetWidth() < dsnSettings.m_ViasMinSize )
  197. {
  198. markers.push_back(
  199. m_markerFactory.NewMarker( refviaPos, refvia, DRCE_TOO_SMALL_VIA ) );
  200. if( !handleNewMarker() )
  201. return false;
  202. }
  203. if( refvia->GetDrillValue() < dsnSettings.m_ViasMinDrill )
  204. {
  205. markers.push_back(
  206. m_markerFactory.NewMarker( refviaPos, refvia, DRCE_TOO_SMALL_VIA_DRILL ) );
  207. if( !handleNewMarker() )
  208. return false;
  209. }
  210. }
  211. // test if via's hole is bigger than its diameter
  212. // This test is necessary since the via hole size and width can be modified
  213. // and a default via hole can be bigger than some vias sizes
  214. if( refvia->GetDrillValue() > refvia->GetWidth() )
  215. {
  216. markers.push_back(
  217. m_markerFactory.NewMarker( refviaPos, refvia, DRCE_VIA_HOLE_BIGGER ) );
  218. if( !handleNewMarker() )
  219. return false;
  220. }
  221. // test if the type of via is allowed due to design rules
  222. if( refvia->GetViaType() == VIA_MICROVIA && !dsnSettings.m_MicroViasAllowed )
  223. {
  224. markers.push_back(
  225. m_markerFactory.NewMarker( refviaPos, refvia, DRCE_MICRO_VIA_NOT_ALLOWED ) );
  226. if( !handleNewMarker() )
  227. return false;
  228. }
  229. // test if the type of via is allowed due to design rules
  230. if( refvia->GetViaType() == VIA_BLIND_BURIED && !dsnSettings.m_BlindBuriedViaAllowed )
  231. {
  232. markers.push_back(
  233. m_markerFactory.NewMarker( refviaPos, refvia, DRCE_BURIED_VIA_NOT_ALLOWED ) );
  234. if( !handleNewMarker() )
  235. return false;
  236. }
  237. // For microvias: test if they are blind vias and only between 2 layers
  238. // because they are used for very small drill size and are drill by laser
  239. // and **only one layer** can be drilled
  240. if( refvia->GetViaType() == VIA_MICROVIA )
  241. {
  242. PCB_LAYER_ID layer1, layer2;
  243. bool err = true;
  244. refvia->LayerPair( &layer1, &layer2 );
  245. if( layer1 > layer2 )
  246. std::swap( layer1, layer2 );
  247. if( layer2 == B_Cu && layer1 == dsnSettings.GetCopperLayerCount() - 2 )
  248. err = false;
  249. else if( layer1 == F_Cu && layer2 == In1_Cu )
  250. err = false;
  251. if( err )
  252. {
  253. markers.push_back( m_markerFactory.NewMarker(
  254. refviaPos, refvia, DRCE_MICRO_VIA_INCORRECT_LAYER_PAIR ) );
  255. if( !handleNewMarker() )
  256. return false;
  257. }
  258. }
  259. }
  260. else // This is a track segment
  261. {
  262. if( aRefSeg->GetWidth() < dsnSettings.m_TrackMinWidth )
  263. {
  264. wxPoint refsegMiddle = ( aRefSeg->GetStart() + aRefSeg->GetEnd() ) / 2;
  265. markers.push_back( m_markerFactory.NewMarker(
  266. refsegMiddle, aRefSeg, DRCE_TOO_SMALL_TRACK_WIDTH ) );
  267. if( !handleNewMarker() )
  268. return false;
  269. }
  270. }
  271. // for a non horizontal or vertical segment Compute the segment angle
  272. // in tenths of degrees and its length
  273. if( delta.x || delta.y )
  274. {
  275. // Compute the segment angle in 0,1 degrees
  276. m_segmAngle = ArcTangente( delta.y, delta.x );
  277. // Compute the segment length: we build an equivalent rotated segment,
  278. // this segment is horizontal, therefore dx = length
  279. RotatePoint( &delta, m_segmAngle ); // delta.x = length, delta.y = 0
  280. }
  281. m_segmLength = delta.x;
  282. /******************************************/
  283. /* Phase 1 : test DRC track to pads : */
  284. /******************************************/
  285. /* Use a dummy pad to test DRC tracks versus holes, for pads not on all copper layers
  286. * but having a hole
  287. * This dummy pad has the size and shape of the hole
  288. * to test tracks to pad hole DRC, using checkClearanceSegmToPad test function.
  289. * Therefore, this dummy pad is a circle or an oval.
  290. * A pad must have a parent because some functions expect a non null parent
  291. * to find the parent board, and some other data
  292. */
  293. MODULE dummymodule( m_pcb ); // Creates a dummy parent
  294. D_PAD dummypad( &dummymodule );
  295. dummypad.SetLayerSet( LSET::AllCuMask() ); // Ensure the hole is on all layers
  296. // Compute the min distance to pads
  297. if( testPads )
  298. {
  299. unsigned pad_count = m_pcb->GetPadCount();
  300. auto pads = m_pcb->GetPads();
  301. for( unsigned ii = 0; ii < pad_count; ++ii )
  302. {
  303. D_PAD* pad = pads[ii];
  304. SEG padSeg( pad->GetPosition(), pad->GetPosition() );
  305. /* No problem if pads are on another layer,
  306. * But if a drill hole exists (a pad on a single layer can have a hole!)
  307. * we must test the hole
  308. */
  309. if( !( pad->GetLayerSet() & layerMask ).any() )
  310. {
  311. /* We must test the pad hole. In order to use the function
  312. * checkClearanceSegmToPad(),a pseudo pad is used, with a shape and a
  313. * size like the hole
  314. */
  315. if( pad->GetDrillSize().x == 0 )
  316. continue;
  317. dummypad.SetSize( pad->GetDrillSize() );
  318. dummypad.SetPosition( pad->GetPosition() );
  319. dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ?
  320. PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE );
  321. dummypad.SetOrientation( pad->GetOrientation() );
  322. m_padToTestPos = dummypad.GetPosition() - origin;
  323. if( !checkClearanceSegmToPad( &dummypad, aRefSeg->GetWidth(),
  324. netclass->GetClearance() ) )
  325. {
  326. markers.push_back( m_markerFactory.NewMarker(
  327. aRefSeg, pad, padSeg, DRCE_TRACK_NEAR_THROUGH_HOLE ) );
  328. if( !handleNewMarker() )
  329. return false;
  330. }
  331. continue;
  332. }
  333. // The pad must be in a net (i.e pt_pad->GetNet() != 0 )
  334. // but no problem if the pad netcode is the current netcode (same net)
  335. if( pad->GetNetCode() // the pad must be connected
  336. && net_code_ref == pad->GetNetCode() ) // the pad net is the same as current net -> Ok
  337. continue;
  338. // DRC for the pad
  339. shape_pos = pad->ShapePos();
  340. m_padToTestPos = shape_pos - origin;
  341. if( !checkClearanceSegmToPad( pad, aRefSeg->GetWidth(), aRefSeg->GetClearance( pad ) ) )
  342. {
  343. markers.push_back(
  344. m_markerFactory.NewMarker( aRefSeg, pad, padSeg, DRCE_TRACK_NEAR_PAD ) );
  345. if( !handleNewMarker() )
  346. return false;
  347. }
  348. }
  349. }
  350. /***********************************************/
  351. /* Phase 2: test DRC with other track segments */
  352. /***********************************************/
  353. // At this point the reference segment is the X axis
  354. // Test the reference segment with other track segments
  355. wxPoint segStartPoint;
  356. wxPoint segEndPoint;
  357. for( track = aStart; track; track = track->Next() )
  358. {
  359. // No problem if segments have the same net code:
  360. if( net_code_ref == track->GetNetCode() )
  361. continue;
  362. // No problem if segment are on different layers :
  363. if( !( layerMask & track->GetLayerSet() ).any() )
  364. continue;
  365. // the minimum distance = clearance plus half the reference track
  366. // width plus half the other track's width
  367. int w_dist = aRefSeg->GetClearance( track );
  368. w_dist += ( aRefSeg->GetWidth() + track->GetWidth() ) / 2;
  369. // Due to many double to int conversions during calculations, which
  370. // create rounding issues,
  371. // the exact clearance margin cannot be really known.
  372. // To avoid false bad DRC detection due to these rounding issues,
  373. // slightly decrease the w_dist (remove one nanometer is enough !)
  374. w_dist -= 1;
  375. // If the reference segment is a via, we test it here
  376. if( aRefSeg->Type() == PCB_VIA_T )
  377. {
  378. delta = track->GetEnd() - track->GetStart();
  379. segStartPoint = aRefSeg->GetStart() - track->GetStart();
  380. wxPoint pos = aRefSeg->GetPosition();
  381. if( track->Type() == PCB_VIA_T )
  382. {
  383. // Test distance between two vias, i.e. two circles, trivial case
  384. if( EuclideanNorm( segStartPoint ) < w_dist )
  385. {
  386. markers.push_back(
  387. m_markerFactory.NewMarker( pos, aRefSeg, track, DRCE_VIA_NEAR_VIA ) );
  388. if( !handleNewMarker() )
  389. return false;
  390. }
  391. }
  392. else // test via to segment
  393. {
  394. // Compute l'angle du segment a tester;
  395. double angle = ArcTangente( delta.y, delta.x );
  396. // Compute new coordinates ( the segment become horizontal)
  397. RotatePoint( &delta, angle );
  398. RotatePoint( &segStartPoint, angle );
  399. if( !checkMarginToCircle( segStartPoint, w_dist, delta.x ) )
  400. {
  401. markers.push_back(
  402. m_markerFactory.NewMarker( pos, aRefSeg, track, DRCE_VIA_NEAR_TRACK ) );
  403. if( !handleNewMarker() )
  404. return false;
  405. }
  406. }
  407. continue;
  408. }
  409. /* We compute segStartPoint, segEndPoint = starting and ending point coordinates for
  410. * the segment to test in the new axis : the new X axis is the
  411. * reference segment. We must translate and rotate the segment to test
  412. */
  413. segStartPoint = track->GetStart() - origin;
  414. segEndPoint = track->GetEnd() - origin;
  415. RotatePoint( &segStartPoint, m_segmAngle );
  416. RotatePoint( &segEndPoint, m_segmAngle );
  417. SEG seg( segStartPoint, segEndPoint );
  418. if( track->Type() == PCB_VIA_T )
  419. {
  420. if( checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
  421. continue;
  422. markers.push_back(
  423. m_markerFactory.NewMarker( aRefSeg, track, seg, DRCE_TRACK_NEAR_VIA ) );
  424. if( !handleNewMarker() )
  425. return false;
  426. }
  427. /* We have changed axis:
  428. * the reference segment is Horizontal.
  429. * 3 cases : the segment to test can be parallel, perpendicular or have another direction
  430. */
  431. if( segStartPoint.y == segEndPoint.y ) // parallel segments
  432. {
  433. if( abs( segStartPoint.y ) >= w_dist )
  434. continue;
  435. // Ensure segStartPoint.x <= segEndPoint.x
  436. if( segStartPoint.x > segEndPoint.x )
  437. std::swap( segStartPoint.x, segEndPoint.x );
  438. if( segStartPoint.x > ( -w_dist ) && segStartPoint.x < ( m_segmLength + w_dist ) )
  439. {
  440. // the start point is inside the reference range
  441. // X........
  442. // O--REF--+
  443. // Fine test : we consider the rounded shape of each end of the track segment:
  444. if( segStartPoint.x >= 0 && segStartPoint.x <= m_segmLength )
  445. {
  446. markers.push_back(
  447. m_markerFactory.NewMarker( aRefSeg, track, seg, DRCE_TRACK_ENDS1 ) );
  448. if( !handleNewMarker() )
  449. return false;
  450. }
  451. if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
  452. {
  453. markers.push_back(
  454. m_markerFactory.NewMarker( aRefSeg, track, seg, DRCE_TRACK_ENDS2 ) );
  455. if( !handleNewMarker() )
  456. return false;
  457. }
  458. }
  459. if( segEndPoint.x > ( -w_dist ) && segEndPoint.x < ( m_segmLength + w_dist ) )
  460. {
  461. // the end point is inside the reference range
  462. // .....X
  463. // O--REF--+
  464. // Fine test : we consider the rounded shape of the ends
  465. if( segEndPoint.x >= 0 && segEndPoint.x <= m_segmLength )
  466. {
  467. markers.push_back(
  468. m_markerFactory.NewMarker( aRefSeg, track, seg, DRCE_TRACK_ENDS3 ) );
  469. if( !handleNewMarker() )
  470. return false;
  471. }
  472. if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) )
  473. {
  474. markers.push_back(
  475. m_markerFactory.NewMarker( aRefSeg, track, seg, DRCE_TRACK_ENDS4 ) );
  476. if( !handleNewMarker() )
  477. return false;
  478. }
  479. }
  480. if( segStartPoint.x <= 0 && segEndPoint.x >= 0 )
  481. {
  482. // the segment straddles the reference range (this actually only
  483. // checks if it straddles the origin, because the other cases where already
  484. // handled)
  485. // X.............X
  486. // O--REF--+
  487. markers.push_back( m_markerFactory.NewMarker(
  488. aRefSeg, track, seg, DRCE_TRACK_SEGMENTS_TOO_CLOSE ) );
  489. if( !handleNewMarker() )
  490. return false;
  491. }
  492. }
  493. else if( segStartPoint.x == segEndPoint.x ) // perpendicular segments
  494. {
  495. if( segStartPoint.x <= -w_dist || segStartPoint.x >= m_segmLength + w_dist )
  496. continue;
  497. // Test if segments are crossing
  498. if( segStartPoint.y > segEndPoint.y )
  499. std::swap( segStartPoint.y, segEndPoint.y );
  500. if( ( segStartPoint.y < 0 ) && ( segEndPoint.y > 0 ) )
  501. {
  502. markers.push_back(
  503. m_markerFactory.NewMarker( aRefSeg, track, seg, DRCE_TRACKS_CROSSING ) );
  504. if( !handleNewMarker() )
  505. return false;
  506. }
  507. // At this point the drc error is due to an end near a reference segm end
  508. if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
  509. {
  510. markers.push_back(
  511. m_markerFactory.NewMarker( aRefSeg, track, seg, DRCE_ENDS_PROBLEM1 ) );
  512. if( !handleNewMarker() )
  513. return false;
  514. }
  515. if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) )
  516. {
  517. markers.push_back(
  518. m_markerFactory.NewMarker( aRefSeg, track, seg, DRCE_ENDS_PROBLEM2 ) );
  519. if( !handleNewMarker() )
  520. return false;
  521. }
  522. }
  523. else // segments quelconques entre eux
  524. {
  525. // calcul de la "surface de securite du segment de reference
  526. // First rought 'and fast) test : the track segment is like a rectangle
  527. m_xcliplo = m_ycliplo = -w_dist;
  528. m_xcliphi = m_segmLength + w_dist;
  529. m_ycliphi = w_dist;
  530. // A fine test is needed because a serment is not exactly a
  531. // rectangle, it has rounded ends
  532. if( !checkLine( segStartPoint, segEndPoint ) )
  533. {
  534. /* 2eme passe : the track has rounded ends.
  535. * we must a fine test for each rounded end and the
  536. * rectangular zone
  537. */
  538. m_xcliplo = 0;
  539. m_xcliphi = m_segmLength;
  540. if( !checkLine( segStartPoint, segEndPoint ) )
  541. {
  542. markers.push_back(
  543. m_markerFactory.NewMarker( aRefSeg, track, seg, DRCE_ENDS_PROBLEM3 ) );
  544. if( !handleNewMarker() )
  545. return false;
  546. }
  547. else // The drc error is due to the starting or the ending point of the reference segment
  548. {
  549. // Test the starting and the ending point
  550. segStartPoint = track->GetStart();
  551. segEndPoint = track->GetEnd();
  552. delta = segEndPoint - segStartPoint;
  553. // Compute the segment orientation (angle) en 0,1 degre
  554. double angle = ArcTangente( delta.y, delta.x );
  555. // Compute the segment length: delta.x = length after rotation
  556. RotatePoint( &delta, angle );
  557. /* Comute the reference segment coordinates relatives to a
  558. * X axis = current tested segment
  559. */
  560. wxPoint relStartPos = aRefSeg->GetStart() - segStartPoint;
  561. wxPoint relEndPos = aRefSeg->GetEnd() - segStartPoint;
  562. RotatePoint( &relStartPos, angle );
  563. RotatePoint( &relEndPos, angle );
  564. if( !checkMarginToCircle( relStartPos, w_dist, delta.x ) )
  565. {
  566. markers.push_back( m_markerFactory.NewMarker(
  567. aRefSeg, track, seg, DRCE_ENDS_PROBLEM4 ) );
  568. if( !handleNewMarker() )
  569. return false;
  570. }
  571. if( !checkMarginToCircle( relEndPos, w_dist, delta.x ) )
  572. {
  573. markers.push_back( m_markerFactory.NewMarker(
  574. aRefSeg, track, seg, DRCE_ENDS_PROBLEM5 ) );
  575. if( !handleNewMarker() )
  576. return false;
  577. }
  578. }
  579. }
  580. }
  581. }
  582. /***********************************************/
  583. /* Phase 3: test DRC with zones */
  584. /***********************************************/
  585. SEG refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
  586. for( ZONE_CONTAINER* zone : m_pcb->Zones() )
  587. {
  588. if( zone->GetFilledPolysList().IsEmpty() || zone->GetIsKeepout() )
  589. continue;
  590. if( !( layerMask & zone->GetLayerSet() ).any() )
  591. continue;
  592. if( zone->GetNetCode() && zone->GetNetCode() == net_code_ref )
  593. continue;
  594. int clearance = zone->GetClearance( aRefSeg );
  595. SHAPE_POLY_SET* outline = const_cast<SHAPE_POLY_SET*>( &zone->GetFilledPolysList() );
  596. if( outline->Distance( refSeg, aRefSeg->GetWidth() ) < clearance )
  597. addMarkerToPcb( m_markerFactory.NewMarker( aRefSeg, zone, DRCE_TRACK_NEAR_ZONE ) );
  598. }
  599. /***********************************************/
  600. /* Phase 4: test DRC with to board edge */
  601. /***********************************************/
  602. {
  603. SEG test_seg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
  604. // the minimum distance = clearance plus half the reference track
  605. // width. Board edges do not have width or clearance values, so we
  606. // look for simple crossing.
  607. SEG::ecoord w_dist = aRefSeg->GetClearance() + aRefSeg->GetWidth() / 2;
  608. w_dist *= w_dist;
  609. for( auto it = m_board_outlines.IterateSegmentsWithHoles(); it; it++ )
  610. {
  611. if( test_seg.SquaredDistance( *it ) < w_dist )
  612. {
  613. auto pt = test_seg.NearestPoint( *it );
  614. markers.push_back( m_markerFactory.NewMarker(
  615. wxPoint( pt.x, pt.y ), aRefSeg, DRCE_TRACK_NEAR_EDGE ) );
  616. if( !handleNewMarker() )
  617. return false;
  618. }
  619. }
  620. }
  621. if( markers.size() > 0 )
  622. {
  623. commitMarkers();
  624. return false;
  625. }
  626. else
  627. return true;
  628. }
  629. bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex )
  630. {
  631. if( !aArea->IsOnCopperLayer() ) // Cannot have a Drc error if not on copper layer
  632. return true;
  633. // Get polygon, contour and vertex index.
  634. SHAPE_POLY_SET::VERTEX_INDEX index;
  635. // If the vertex does not exist, there is no conflict
  636. if( !aArea->Outline()->GetRelativeIndices( aCornerIndex, &index ) )
  637. return true;
  638. // Retrieve the selected contour
  639. SHAPE_LINE_CHAIN contour;
  640. contour = aArea->Outline()->Polygon( index.m_polygon )[index.m_contour];
  641. // Retrieve the segment that starts at aCornerIndex-th corner.
  642. SEG selectedSegment = contour.Segment( index.m_vertex );
  643. VECTOR2I start = selectedSegment.A;
  644. VECTOR2I end = selectedSegment.B;
  645. // iterate through all areas
  646. for( int ia2 = 0; ia2 < m_pcb->GetAreaCount(); ia2++ )
  647. {
  648. ZONE_CONTAINER* area_to_test = m_pcb->GetArea( ia2 );
  649. int zone_clearance = std::max( area_to_test->GetZoneClearance(),
  650. aArea->GetZoneClearance() );
  651. // test for same layer
  652. if( area_to_test->GetLayer() != aArea->GetLayer() )
  653. continue;
  654. // Test for same net
  655. if( ( aArea->GetNetCode() == area_to_test->GetNetCode() ) && (aArea->GetNetCode() >= 0) )
  656. continue;
  657. // test for same priority
  658. if( area_to_test->GetPriority() != aArea->GetPriority() )
  659. continue;
  660. // test for same type
  661. if( area_to_test->GetIsKeepout() != aArea->GetIsKeepout() )
  662. continue;
  663. // For keepout, there is no clearance, so use a minimal value for it
  664. // use 1, not 0 as value to avoid some issues in tests
  665. if( area_to_test->GetIsKeepout() )
  666. zone_clearance = 1;
  667. // test for ending line inside area_to_test
  668. if( area_to_test->Outline()->Contains( end ) )
  669. {
  670. wxPoint pos( end.x, end.y );
  671. m_currentMarker =
  672. m_markerFactory.NewMarker( pos, aArea, area_to_test, DRCE_ZONES_INTERSECT );
  673. return false;
  674. }
  675. // now test spacing between areas
  676. int ax1 = start.x;
  677. int ay1 = start.y;
  678. int ax2 = end.x;
  679. int ay2 = end.y;
  680. // Iterate through all edges in the polygon.
  681. SHAPE_POLY_SET::SEGMENT_ITERATOR iterator;
  682. for( iterator = area_to_test->Outline()->IterateSegmentsWithHoles(); iterator; iterator++ )
  683. {
  684. SEG segment = *iterator;
  685. int bx1 = segment.A.x;
  686. int by1 = segment.A.y;
  687. int bx2 = segment.B.x;
  688. int by2 = segment.B.y;
  689. int x, y; // variables containing the intersecting point coordinates
  690. int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2,
  691. 0,
  692. ax1, ay1, ax2, ay2,
  693. 0,
  694. zone_clearance,
  695. &x, &y );
  696. if( d < zone_clearance )
  697. {
  698. // COPPERAREA_COPPERAREA error : edge intersect or too close
  699. m_currentMarker = m_markerFactory.NewMarker(
  700. wxPoint( x, y ), aArea, area_to_test, DRCE_ZONES_TOO_CLOSE );
  701. return false;
  702. }
  703. }
  704. }
  705. return true;
  706. }
  707. bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
  708. {
  709. int dist;
  710. double pad_angle;
  711. // Get the clearance between the 2 pads. this is the min distance between aRefPad and aPad
  712. int dist_min = aRefPad->GetClearance( aPad );
  713. // relativePadPos is the aPad shape position relative to the aRefPad shape position
  714. wxPoint relativePadPos = aPad->ShapePos() - aRefPad->ShapePos();
  715. dist = KiROUND( EuclideanNorm( relativePadPos ) );
  716. // Quick test: Clearance is OK if the bounding circles are further away than "dist_min"
  717. int delta = dist - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius();
  718. if( delta >= dist_min )
  719. return true;
  720. /* Here, pads are near and DRC depend on the pad shapes
  721. * We must compare distance using a fine shape analysis
  722. * Because a circle or oval shape is the easier shape to test, try to have
  723. * aRefPad shape type = PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL.
  724. * if aRefPad = TRAP. and aPad = RECT, also swap pads
  725. * Swap aRefPad and aPad if needed
  726. */
  727. bool swap_pads;
  728. swap_pads = false;
  729. // swap pads to make comparisons easier
  730. // Note also a ROUNDRECT pad with a corner radius = r can be considered as
  731. // a smaller RECT (size - 2*r) with a clearance increased by r
  732. // priority is aRefPad = ROUND then OVAL then RECT/ROUNDRECT then other
  733. if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_SHAPE_CIRCLE )
  734. {
  735. // pad ref shape is here oval, rect, roundrect, trapezoid or custom
  736. switch( aPad->GetShape() )
  737. {
  738. case PAD_SHAPE_CIRCLE:
  739. swap_pads = true;
  740. break;
  741. case PAD_SHAPE_OVAL:
  742. swap_pads = true;
  743. break;
  744. case PAD_SHAPE_RECT:
  745. case PAD_SHAPE_ROUNDRECT:
  746. if( aRefPad->GetShape() != PAD_SHAPE_OVAL )
  747. swap_pads = true;
  748. break;
  749. case PAD_SHAPE_TRAPEZOID:
  750. case PAD_SHAPE_CUSTOM:
  751. break;
  752. }
  753. }
  754. if( swap_pads )
  755. {
  756. std::swap( aRefPad, aPad );
  757. relativePadPos = -relativePadPos;
  758. }
  759. // corners of aRefPad (used only for rect/roundrect/trap pad)
  760. wxPoint polyref[4];
  761. // corners of aRefPad (used only for custom pad)
  762. SHAPE_POLY_SET polysetref;
  763. // corners of aPad (used only for rect/roundrect/trap pad)
  764. wxPoint polycompare[4];
  765. // corners of aPad (used only custom pad)
  766. SHAPE_POLY_SET polysetcompare;
  767. /* Because pad exchange, aRefPad shape is PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL,
  768. * if one of the 2 pads was a PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL.
  769. * Therefore, if aRefPad is a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID,
  770. * aPad is also a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID
  771. */
  772. bool diag = true;
  773. switch( aRefPad->GetShape() )
  774. {
  775. case PAD_SHAPE_CIRCLE:
  776. /* One can use checkClearanceSegmToPad to test clearance
  777. * aRefPad is like a track segment with a null length and a witdth = GetSize().x
  778. */
  779. m_segmLength = 0;
  780. m_segmAngle = 0;
  781. m_segmEnd.x = m_segmEnd.y = 0;
  782. m_padToTestPos = relativePadPos;
  783. diag = checkClearanceSegmToPad( aPad, aRefPad->GetSize().x, dist_min );
  784. break;
  785. case PAD_SHAPE_TRAPEZOID:
  786. case PAD_SHAPE_ROUNDRECT:
  787. case PAD_SHAPE_RECT:
  788. case PAD_SHAPE_CUSTOM:
  789. // pad_angle = pad orient relative to the aRefPad orient
  790. pad_angle = aRefPad->GetOrientation() + aPad->GetOrientation();
  791. NORMALIZE_ANGLE_POS( pad_angle );
  792. if( aRefPad->GetShape() == PAD_SHAPE_ROUNDRECT )
  793. {
  794. int padRadius = aRefPad->GetRoundRectCornerRadius();
  795. dist_min += padRadius;
  796. GetRoundRectCornerCenters( polyref, padRadius, wxPoint( 0, 0 ),
  797. aRefPad->GetSize(), aRefPad->GetOrientation() );
  798. }
  799. else if( aRefPad->GetShape() == PAD_SHAPE_CUSTOM )
  800. {
  801. polysetref.Append( aRefPad->GetCustomShapeAsPolygon() );
  802. // The reference pad can be rotated. calculate the rotated
  803. // coordiantes ( note, the ref pad position is the origin of
  804. // coordinates for this drc test)
  805. aRefPad->CustomShapeAsPolygonToBoardPosition( &polysetref,
  806. wxPoint( 0, 0 ), aRefPad->GetOrientation() );
  807. }
  808. else
  809. {
  810. // BuildPadPolygon has meaning for rect a trapeziod shapes
  811. // and returns the 4 corners
  812. aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() );
  813. }
  814. switch( aPad->GetShape() )
  815. {
  816. case PAD_SHAPE_ROUNDRECT:
  817. case PAD_SHAPE_RECT:
  818. case PAD_SHAPE_TRAPEZOID:
  819. case PAD_SHAPE_CUSTOM:
  820. if( aPad->GetShape() == PAD_SHAPE_ROUNDRECT )
  821. {
  822. int padRadius = aPad->GetRoundRectCornerRadius();
  823. dist_min += padRadius;
  824. GetRoundRectCornerCenters( polycompare, padRadius, relativePadPos,
  825. aPad->GetSize(), aPad->GetOrientation() );
  826. }
  827. else if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
  828. {
  829. polysetcompare.Append( aPad->GetCustomShapeAsPolygon() );
  830. // The pad to compare can be rotated. calculate the rotated
  831. // coordinattes ( note, the pad to compare position
  832. // is the relativePadPos for this drc test
  833. aPad->CustomShapeAsPolygonToBoardPosition( &polysetcompare,
  834. relativePadPos, aPad->GetOrientation() );
  835. }
  836. else
  837. {
  838. aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() );
  839. // Move aPad shape to relativePadPos
  840. for( int ii = 0; ii < 4; ii++ )
  841. polycompare[ii] += relativePadPos;
  842. }
  843. // And now test polygons: We have 3 cases:
  844. // one poly is complex and the other is basic (has only 4 corners)
  845. // both polys are complex
  846. // both polys are basic (have only 4 corners) the most usual case
  847. if( polysetref.OutlineCount() && polysetcompare.OutlineCount() == 0)
  848. {
  849. const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
  850. // And now test polygons:
  851. if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
  852. polycompare, 4, dist_min ) )
  853. diag = false;
  854. }
  855. else if( polysetref.OutlineCount() == 0 && polysetcompare.OutlineCount())
  856. {
  857. const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 );
  858. // And now test polygons:
  859. if( !poly2polyDRC( (wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(),
  860. polyref, 4, dist_min ) )
  861. diag = false;
  862. }
  863. else if( polysetref.OutlineCount() && polysetcompare.OutlineCount() )
  864. {
  865. const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
  866. const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 );
  867. // And now test polygons:
  868. if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
  869. (wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(), dist_min ) )
  870. diag = false;
  871. }
  872. else if( !poly2polyDRC( polyref, 4, polycompare, 4, dist_min ) )
  873. diag = false;
  874. break;
  875. default:
  876. wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape %d" ), aPad->GetShape() );
  877. break;
  878. }
  879. break;
  880. case PAD_SHAPE_OVAL: /* an oval pad is like a track segment */
  881. {
  882. /* Create a track segment with same dimensions as the oval aRefPad
  883. * and use checkClearanceSegmToPad function to test aPad to aRefPad clearance
  884. */
  885. int segm_width;
  886. m_segmAngle = aRefPad->GetOrientation(); // Segment orient.
  887. if( aRefPad->GetSize().y < aRefPad->GetSize().x ) // Build an horizontal equiv segment
  888. {
  889. segm_width = aRefPad->GetSize().y;
  890. m_segmLength = aRefPad->GetSize().x - aRefPad->GetSize().y;
  891. }
  892. else // Vertical oval: build an horizontal equiv segment and rotate 90.0 deg
  893. {
  894. segm_width = aRefPad->GetSize().x;
  895. m_segmLength = aRefPad->GetSize().y - aRefPad->GetSize().x;
  896. m_segmAngle += 900;
  897. }
  898. /* the start point must be 0,0 and currently relativePadPos
  899. * is relative the center of pad coordinate */
  900. wxPoint segstart;
  901. segstart.x = -m_segmLength / 2; // Start point coordinate of the horizontal equivalent segment
  902. RotatePoint( &segstart, m_segmAngle ); // actual start point coordinate of the equivalent segment
  903. // Calculate segment end position relative to the segment origin
  904. m_segmEnd.x = -2 * segstart.x;
  905. m_segmEnd.y = -2 * segstart.y;
  906. // Recalculate the equivalent segment angle in 0,1 degrees
  907. // to prepare a call to checkClearanceSegmToPad()
  908. m_segmAngle = ArcTangente( m_segmEnd.y, m_segmEnd.x );
  909. // move pad position relative to the segment origin
  910. m_padToTestPos = relativePadPos - segstart;
  911. // Use segment to pad check to test the second pad:
  912. diag = checkClearanceSegmToPad( aPad, segm_width, dist_min );
  913. break;
  914. }
  915. default:
  916. wxLogDebug( wxT( "DRC::checkClearancePadToPad: unknown pad shape" ) );
  917. break;
  918. }
  919. return diag;
  920. }
  921. /* test if distance between a segment is > aMinDist
  922. * segment start point is assumed in (0,0) and segment start point in m_segmEnd
  923. * and its orientation is m_segmAngle (m_segmAngle must be already initialized)
  924. * and have aSegmentWidth.
  925. */
  926. bool DRC::checkClearanceSegmToPad( const D_PAD* aPad, int aSegmentWidth, int aMinDist )
  927. {
  928. // Note:
  929. // we are using a horizontal segment for test, because we know here
  930. // only the length and orientation+ of the segment
  931. // Therefore the coordinates of the shape of pad to compare
  932. // must be calculated in a axis system rotated by m_segmAngle
  933. // and centered to the segment origin, before they can be tested
  934. // against the segment
  935. // We are using:
  936. // m_padToTestPos the position of the pad shape in this axis system
  937. // m_segmAngle the axis system rotation
  938. int segmHalfWidth = aSegmentWidth / 2;
  939. int distToLine = segmHalfWidth + aMinDist;
  940. wxSize padHalfsize; // half dimension of the pad
  941. if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
  942. {
  943. // For a custom pad, the pad size has no meaning, we only can
  944. // use the bounding radius
  945. padHalfsize.x = padHalfsize.y = aPad->GetBoundingRadius();
  946. }
  947. else
  948. {
  949. padHalfsize = aPad->GetSize() / 2;
  950. }
  951. if( aPad->GetShape() == PAD_SHAPE_TRAPEZOID ) // The size is bigger, due to GetDelta() extra size
  952. {
  953. padHalfsize.x += std::abs(aPad->GetDelta().y) / 2; // Remember: GetDelta().y is the GetSize().x change
  954. padHalfsize.y += std::abs(aPad->GetDelta().x) / 2; // Remember: GetDelta().x is the GetSize().y change
  955. }
  956. if( aPad->GetShape() == PAD_SHAPE_CIRCLE )
  957. {
  958. /* Easy case: just test the distance between segment and pad centre
  959. * calculate pad coordinates in the X,Y axis with X axis = segment to test
  960. */
  961. RotatePoint( &m_padToTestPos, m_segmAngle );
  962. return checkMarginToCircle( m_padToTestPos, distToLine + padHalfsize.x, m_segmLength );
  963. }
  964. /* calculate the bounding box of the pad, including the clearance and the segment width
  965. * if the line from 0 to m_segmEnd does not intersect this bounding box,
  966. * the clearance is always OK
  967. * But if intersect, a better analysis of the pad shape must be done.
  968. */
  969. m_xcliplo = m_padToTestPos.x - distToLine - padHalfsize.x;
  970. m_ycliplo = m_padToTestPos.y - distToLine - padHalfsize.y;
  971. m_xcliphi = m_padToTestPos.x + distToLine + padHalfsize.x;
  972. m_ycliphi = m_padToTestPos.y + distToLine + padHalfsize.y;
  973. wxPoint startPoint( 0, 0 );
  974. wxPoint endPoint = m_segmEnd;
  975. double orient = aPad->GetOrientation();
  976. RotatePoint( &startPoint, m_padToTestPos, -orient );
  977. RotatePoint( &endPoint, m_padToTestPos, -orient );
  978. if( checkLine( startPoint, endPoint ) )
  979. return true;
  980. /* segment intersects the bounding box. But there is not always a DRC error.
  981. * A fine analysis of the pad shape must be done.
  982. */
  983. switch( aPad->GetShape() )
  984. {
  985. case PAD_SHAPE_CIRCLE:
  986. // This case was already tested, so it cannot be found here.
  987. // it is here just to avoid compil warning, and to remember
  988. // it is already tested.
  989. return false;
  990. case PAD_SHAPE_OVAL:
  991. {
  992. /* an oval is a complex shape, but is a rectangle and 2 circles
  993. * these 3 basic shapes are more easy to test.
  994. *
  995. * In calculations we are using a vertical or horizontal oval shape
  996. * (i.e. a vertical or horizontal rounded segment)
  997. */
  998. wxPoint cstart = m_padToTestPos;
  999. wxPoint cend = m_padToTestPos; // center of each circle
  1000. int delta = std::abs( padHalfsize.y - padHalfsize.x );
  1001. int radius = std::min( padHalfsize.y, padHalfsize.x );
  1002. if( padHalfsize.x > padHalfsize.y ) // horizontal equivalent segment
  1003. {
  1004. cstart.x -= delta;
  1005. cend.x += delta;
  1006. // Build the rectangular clearance area between the two circles
  1007. // the rect starts at cstart.x and ends at cend.x and its height
  1008. // is (radius + distToLine)*2
  1009. m_xcliplo = cstart.x;
  1010. m_ycliplo = cstart.y - radius - distToLine;
  1011. m_xcliphi = cend.x;
  1012. m_ycliphi = cend.y + radius + distToLine;
  1013. }
  1014. else // vertical equivalent segment
  1015. {
  1016. cstart.y -= delta;
  1017. cend.y += delta;
  1018. // Build the rectangular clearance area between the two circles
  1019. // the rect starts at cstart.y and ends at cend.y and its width
  1020. // is (radius + distToLine)*2
  1021. m_xcliplo = cstart.x - distToLine - radius;
  1022. m_ycliplo = cstart.y;
  1023. m_xcliphi = cend.x + distToLine + radius;
  1024. m_ycliphi = cend.y;
  1025. }
  1026. // Test the rectangular clearance area between the two circles (the rounded ends)
  1027. // If the segment legth is zero, only check the endpoints, skip the rectangle
  1028. if( m_segmLength && !checkLine( startPoint, endPoint ) )
  1029. {
  1030. return false;
  1031. }
  1032. // test the first end
  1033. // Calculate the actual position of the circle, given the pad orientation:
  1034. RotatePoint( &cstart, m_padToTestPos, orient );
  1035. // Calculate the actual position of the circle in the new X,Y axis, relative
  1036. // to the segment:
  1037. RotatePoint( &cstart, m_segmAngle );
  1038. if( !checkMarginToCircle( cstart, radius + distToLine, m_segmLength ) )
  1039. {
  1040. return false;
  1041. }
  1042. // test the second end
  1043. RotatePoint( &cend, m_padToTestPos, orient );
  1044. RotatePoint( &cend, m_segmAngle );
  1045. if( !checkMarginToCircle( cend, radius + distToLine, m_segmLength ) )
  1046. {
  1047. return false;
  1048. }
  1049. }
  1050. break;
  1051. case PAD_SHAPE_ROUNDRECT:
  1052. {
  1053. // a round rect is a smaller rect, with a clearance augmented by the corners radius
  1054. int r = aPad->GetRoundRectCornerRadius();
  1055. padHalfsize.x -= r;
  1056. padHalfsize.y -= r;
  1057. distToLine += r;
  1058. }
  1059. // Fall through
  1060. case PAD_SHAPE_RECT:
  1061. // the area to test is a rounded rectangle.
  1062. // this can be done by testing 2 rectangles and 4 circles (the corners)
  1063. // Testing the first rectangle dimx + distToLine, dimy:
  1064. m_xcliplo = m_padToTestPos.x - padHalfsize.x - distToLine;
  1065. m_ycliplo = m_padToTestPos.y - padHalfsize.y;
  1066. m_xcliphi = m_padToTestPos.x + padHalfsize.x + distToLine;
  1067. m_ycliphi = m_padToTestPos.y + padHalfsize.y;
  1068. if( !checkLine( startPoint, endPoint ) )
  1069. return false;
  1070. // Testing the second rectangle dimx , dimy + distToLine
  1071. m_xcliplo = m_padToTestPos.x - padHalfsize.x;
  1072. m_ycliplo = m_padToTestPos.y - padHalfsize.y - distToLine;
  1073. m_xcliphi = m_padToTestPos.x + padHalfsize.x;
  1074. m_ycliphi = m_padToTestPos.y + padHalfsize.y + distToLine;
  1075. if( !checkLine( startPoint, endPoint ) )
  1076. return false;
  1077. // testing the 4 circles which are the clearance area of each corner:
  1078. // testing the left top corner of the rectangle
  1079. startPoint.x = m_padToTestPos.x - padHalfsize.x;
  1080. startPoint.y = m_padToTestPos.y - padHalfsize.y;
  1081. RotatePoint( &startPoint, m_padToTestPos, orient );
  1082. RotatePoint( &startPoint, m_segmAngle );
  1083. if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
  1084. return false;
  1085. // testing the right top corner of the rectangle
  1086. startPoint.x = m_padToTestPos.x + padHalfsize.x;
  1087. startPoint.y = m_padToTestPos.y - padHalfsize.y;
  1088. RotatePoint( &startPoint, m_padToTestPos, orient );
  1089. RotatePoint( &startPoint, m_segmAngle );
  1090. if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
  1091. return false;
  1092. // testing the left bottom corner of the rectangle
  1093. startPoint.x = m_padToTestPos.x - padHalfsize.x;
  1094. startPoint.y = m_padToTestPos.y + padHalfsize.y;
  1095. RotatePoint( &startPoint, m_padToTestPos, orient );
  1096. RotatePoint( &startPoint, m_segmAngle );
  1097. if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
  1098. return false;
  1099. // testing the right bottom corner of the rectangle
  1100. startPoint.x = m_padToTestPos.x + padHalfsize.x;
  1101. startPoint.y = m_padToTestPos.y + padHalfsize.y;
  1102. RotatePoint( &startPoint, m_padToTestPos, orient );
  1103. RotatePoint( &startPoint, m_segmAngle );
  1104. if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
  1105. return false;
  1106. break;
  1107. case PAD_SHAPE_TRAPEZOID:
  1108. {
  1109. wxPoint poly[4];
  1110. aPad->BuildPadPolygon( poly, wxSize( 0, 0 ), orient );
  1111. // Move shape to m_padToTestPos
  1112. for( int ii = 0; ii < 4; ii++ )
  1113. {
  1114. poly[ii] += m_padToTestPos;
  1115. RotatePoint( &poly[ii], m_segmAngle );
  1116. }
  1117. if( !poly2segmentDRC( poly, 4, wxPoint( 0, 0 ),
  1118. wxPoint(m_segmLength,0), distToLine ) )
  1119. return false;
  1120. }
  1121. break;
  1122. case PAD_SHAPE_CUSTOM:
  1123. {
  1124. SHAPE_POLY_SET polyset;
  1125. polyset.Append( aPad->GetCustomShapeAsPolygon() );
  1126. // The pad can be rotated. calculate the coordinates
  1127. // relatives to the segment being tested
  1128. // Note, the pad position relative to the segment origin
  1129. // is m_padToTestPos
  1130. aPad->CustomShapeAsPolygonToBoardPosition( &polyset,
  1131. m_padToTestPos, orient );
  1132. // Rotate all coordinates by m_segmAngle, because the segment orient
  1133. // is m_segmAngle
  1134. // we are using a horizontal segment for test, because we know here
  1135. // only the lenght and orientation+ of the segment
  1136. // therefore all coordinates of the pad to test must be rotated by
  1137. // m_segmAngle (they are already relative to the segment origin)
  1138. aPad->CustomShapeAsPolygonToBoardPosition( &polyset,
  1139. wxPoint( 0, 0 ), m_segmAngle );
  1140. const SHAPE_LINE_CHAIN& refpoly = polyset.COutline( 0 );
  1141. if( !poly2segmentDRC( (wxPoint*) &refpoly.CPoint( 0 ),
  1142. refpoly.PointCount(),
  1143. wxPoint( 0, 0 ), wxPoint(m_segmLength,0),
  1144. distToLine ) )
  1145. return false;
  1146. }
  1147. break;
  1148. }
  1149. return true;
  1150. }
  1151. /**
  1152. * Helper function checkMarginToCircle
  1153. * Check the distance between a circle (round pad, via or round end of track)
  1154. * and a segment. the segment is expected starting at 0,0, and on the X axis
  1155. * return true if distance >= aRadius
  1156. */
  1157. bool DRC::checkMarginToCircle( wxPoint aCentre, int aRadius, int aLength )
  1158. {
  1159. if( abs( aCentre.y ) >= aRadius ) // trivial case
  1160. return true;
  1161. // Here, distance between aCentre and X axis is < aRadius
  1162. if( (aCentre.x > -aRadius ) && ( aCentre.x < (aLength + aRadius) ) )
  1163. {
  1164. if( (aCentre.x >= 0) && (aCentre.x <= aLength) )
  1165. return false; // aCentre is between the starting point and the ending point of the segm
  1166. if( aCentre.x > aLength ) // aCentre is after the ending point
  1167. aCentre.x -= aLength; // move aCentre to the starting point of the segment
  1168. if( EuclideanNorm( aCentre ) < aRadius )
  1169. // distance between aCentre and the starting point or the ending point is < aRadius
  1170. return false;
  1171. }
  1172. return true;
  1173. }
  1174. // Helper function used in checkLine::
  1175. static inline int USCALE( unsigned arg, unsigned num, unsigned den )
  1176. {
  1177. int ii;
  1178. double result;
  1179. // Trivial check first
  1180. if( !arg || !num)
  1181. return 0;
  1182. // If arg and num are both non-zero but den is zero, we return effective infinite
  1183. if( !den )
  1184. return INT_MAX;
  1185. result = ( (double) arg * num ) / den;
  1186. // Ensure that our result doesn't overflow into the sign bit
  1187. if( result > INT_MAX )
  1188. return INT_MAX;
  1189. ii = KiROUND( ( (double) arg * num ) / den );
  1190. return ii;
  1191. }
  1192. /** Helper function checkLine
  1193. * Test if a line intersects a bounding box (a rectangle)
  1194. * The rectangle is defined by m_xcliplo, m_ycliplo and m_xcliphi, m_ycliphi
  1195. * return true if the line from aSegStart to aSegEnd is outside the bounding box
  1196. */
  1197. bool DRC::checkLine( wxPoint aSegStart, wxPoint aSegEnd )
  1198. {
  1199. #define WHEN_OUTSIDE return true
  1200. #define WHEN_INSIDE
  1201. int temp;
  1202. if( aSegStart.x > aSegEnd.x )
  1203. std::swap( aSegStart, aSegEnd );
  1204. if( (aSegEnd.x <= m_xcliplo) || (aSegStart.x >= m_xcliphi) )
  1205. {
  1206. WHEN_OUTSIDE;
  1207. }
  1208. if( aSegStart.y < aSegEnd.y )
  1209. {
  1210. if( (aSegEnd.y <= m_ycliplo) || (aSegStart.y >= m_ycliphi) )
  1211. {
  1212. WHEN_OUTSIDE;
  1213. }
  1214. if( aSegStart.y < m_ycliplo )
  1215. {
  1216. temp = USCALE( (aSegEnd.x - aSegStart.x), (m_ycliplo - aSegStart.y),
  1217. (aSegEnd.y - aSegStart.y) );
  1218. if( (aSegStart.x += temp) >= m_xcliphi )
  1219. {
  1220. WHEN_OUTSIDE;
  1221. }
  1222. aSegStart.y = m_ycliplo;
  1223. WHEN_INSIDE;
  1224. }
  1225. if( aSegEnd.y > m_ycliphi )
  1226. {
  1227. temp = USCALE( (aSegEnd.x - aSegStart.x), (aSegEnd.y - m_ycliphi),
  1228. (aSegEnd.y - aSegStart.y) );
  1229. if( (aSegEnd.x -= temp) <= m_xcliplo )
  1230. {
  1231. WHEN_OUTSIDE;
  1232. }
  1233. aSegEnd.y = m_ycliphi;
  1234. WHEN_INSIDE;
  1235. }
  1236. if( aSegStart.x < m_xcliplo )
  1237. {
  1238. temp = USCALE( (aSegEnd.y - aSegStart.y), (m_xcliplo - aSegStart.x),
  1239. (aSegEnd.x - aSegStart.x) );
  1240. aSegStart.y += temp;
  1241. aSegStart.x = m_xcliplo;
  1242. WHEN_INSIDE;
  1243. }
  1244. if( aSegEnd.x > m_xcliphi )
  1245. {
  1246. temp = USCALE( (aSegEnd.y - aSegStart.y), (aSegEnd.x - m_xcliphi),
  1247. (aSegEnd.x - aSegStart.x) );
  1248. aSegEnd.y -= temp;
  1249. aSegEnd.x = m_xcliphi;
  1250. WHEN_INSIDE;
  1251. }
  1252. }
  1253. else
  1254. {
  1255. if( (aSegStart.y <= m_ycliplo) || (aSegEnd.y >= m_ycliphi) )
  1256. {
  1257. WHEN_OUTSIDE;
  1258. }
  1259. if( aSegStart.y > m_ycliphi )
  1260. {
  1261. temp = USCALE( (aSegEnd.x - aSegStart.x), (aSegStart.y - m_ycliphi),
  1262. (aSegStart.y - aSegEnd.y) );
  1263. if( (aSegStart.x += temp) >= m_xcliphi )
  1264. {
  1265. WHEN_OUTSIDE;
  1266. }
  1267. aSegStart.y = m_ycliphi;
  1268. WHEN_INSIDE;
  1269. }
  1270. if( aSegEnd.y < m_ycliplo )
  1271. {
  1272. temp = USCALE( (aSegEnd.x - aSegStart.x), (m_ycliplo - aSegEnd.y),
  1273. (aSegStart.y - aSegEnd.y) );
  1274. if( (aSegEnd.x -= temp) <= m_xcliplo )
  1275. {
  1276. WHEN_OUTSIDE;
  1277. }
  1278. aSegEnd.y = m_ycliplo;
  1279. WHEN_INSIDE;
  1280. }
  1281. if( aSegStart.x < m_xcliplo )
  1282. {
  1283. temp = USCALE( (aSegStart.y - aSegEnd.y), (m_xcliplo - aSegStart.x),
  1284. (aSegEnd.x - aSegStart.x) );
  1285. aSegStart.y -= temp;
  1286. aSegStart.x = m_xcliplo;
  1287. WHEN_INSIDE;
  1288. }
  1289. if( aSegEnd.x > m_xcliphi )
  1290. {
  1291. temp = USCALE( (aSegStart.y - aSegEnd.y), (aSegEnd.x - m_xcliphi),
  1292. (aSegEnd.x - aSegStart.x) );
  1293. aSegEnd.y += temp;
  1294. aSegEnd.x = m_xcliphi;
  1295. WHEN_INSIDE;
  1296. }
  1297. }
  1298. // Do not divide here to avoid rounding errors
  1299. if( ( (aSegEnd.x + aSegStart.x) < m_xcliphi * 2 )
  1300. && ( (aSegEnd.x + aSegStart.x) > m_xcliplo * 2) \
  1301. && ( (aSegEnd.y + aSegStart.y) < m_ycliphi * 2 )
  1302. && ( (aSegEnd.y + aSegStart.y) > m_ycliplo * 2 ) )
  1303. {
  1304. return false;
  1305. }
  1306. else
  1307. {
  1308. return true;
  1309. }
  1310. }