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.

1410 lines
52 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. /* compare 2 convex polygons and return true if distance > aDist
  45. * i.e if for each edge of the first polygon distance from each edge of the other polygon
  46. * is >= aDist
  47. */
  48. bool poly2polyDRC( wxPoint* aTref, int aTrefCount,
  49. wxPoint* aTcompare, int aTcompareCount, int aDist )
  50. {
  51. /* Test if one polygon is contained in the other and thus the polygon overlap.
  52. * This case is not covered by the following check if one polygone is
  53. * completely contained in the other (because edges don't intersect)!
  54. */
  55. if( TestPointInsidePolygon( aTref, aTrefCount, aTcompare[0] ) )
  56. return false;
  57. if( TestPointInsidePolygon( aTcompare, aTcompareCount, aTref[0] ) )
  58. return false;
  59. for( int ii = 0, jj = aTrefCount - 1; ii < aTrefCount; jj = ii, ii++ )
  60. { // for all edges in aTref
  61. for( int kk = 0, ll = aTcompareCount - 1; kk < aTcompareCount; ll = kk, kk++ )
  62. { // for all edges in aTcompare
  63. double d;
  64. int intersect = TestForIntersectionOfStraightLineSegments(
  65. aTref[ii].x, aTref[ii].y, aTref[jj].x, aTref[jj].y,
  66. aTcompare[kk].x, aTcompare[kk].y, aTcompare[ll].x, aTcompare[ll].y,
  67. NULL, NULL, &d );
  68. if( intersect || ( d < aDist ) )
  69. return false;
  70. }
  71. }
  72. return true;
  73. }
  74. /* compare a trapezoids (can be rectangle) and a segment and return true if distance > aDist
  75. */
  76. bool poly2segmentDRC( wxPoint* aTref, int aTrefCount, wxPoint aSegStart, wxPoint aSegEnd, int aDist )
  77. {
  78. /* Test if the segment is contained in the polygon.
  79. * This case is not covered by the following check if the segment is
  80. * completely contained in the polygon (because edges don't intersect)!
  81. */
  82. if( TestPointInsidePolygon( aTref, aTrefCount, aSegStart ) )
  83. return false;
  84. for( int ii = 0, jj = aTrefCount-1; ii < aTrefCount; jj = ii, ii++ )
  85. { // for all edges in polygon
  86. double d;
  87. int intersect = TestForIntersectionOfStraightLineSegments(
  88. aTref[ii].x, aTref[ii].y, aTref[jj].x, aTref[jj].y,
  89. aSegStart.x, aSegStart.y, aSegEnd.x, aSegEnd.y,
  90. NULL, NULL, &d );
  91. if( intersect || ( d < aDist) )
  92. return false;
  93. }
  94. return true;
  95. }
  96. /* compare a polygon to a point and return true if distance > aDist
  97. * do not use this function for horizontal or vertical rectangles
  98. * because there is a faster an easier way to compare the distance
  99. */
  100. bool convex2pointDRC( wxPoint* aTref, int aTrefCount, wxPoint aPcompare, int aDist )
  101. {
  102. /* Test if aPcompare point is contained in the polygon.
  103. * This case is not covered by the following check if this point is inside the polygon
  104. */
  105. if( TestPointInsidePolygon( aTref, aTrefCount, aPcompare ) )
  106. {
  107. return false;
  108. }
  109. // Test distance between aPcompare and each segment of the polygon:
  110. for( int ii = 0, jj = aTrefCount - 1; ii < aTrefCount; jj = ii, ii++ ) // for all edge in polygon
  111. {
  112. if( TestSegmentHit( aPcompare, aTref[ii], aTref[jj], aDist ) )
  113. return false;
  114. }
  115. return true;
  116. }
  117. bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
  118. {
  119. TRACK* track;
  120. wxPoint delta; // length on X and Y axis of segments
  121. LSET layerMask;
  122. int net_code_ref;
  123. wxPoint shape_pos;
  124. NETCLASSPTR netclass = aRefSeg->GetNetClass();
  125. BOARD_DESIGN_SETTINGS& dsnSettings = m_pcb->GetDesignSettings();
  126. /* In order to make some calculations more easier or faster,
  127. * pads and tracks coordinates will be made relative to the reference segment origin
  128. */
  129. wxPoint origin = aRefSeg->GetStart(); // origin will be the origin of other coordinates
  130. m_segmEnd = delta = aRefSeg->GetEnd() - origin;
  131. m_segmAngle = 0;
  132. layerMask = aRefSeg->GetLayerSet();
  133. net_code_ref = aRefSeg->GetNetCode();
  134. // Phase 0 : Test vias
  135. if( aRefSeg->Type() == PCB_VIA_T )
  136. {
  137. const VIA *refvia = static_cast<const VIA*>( aRefSeg );
  138. // test if the via size is smaller than minimum
  139. if( refvia->GetViaType() == VIA_MICROVIA )
  140. {
  141. if( refvia->GetWidth() < dsnSettings.m_MicroViasMinSize )
  142. {
  143. m_currentMarker = fillMarker( refvia, NULL,
  144. DRCE_TOO_SMALL_MICROVIA, m_currentMarker );
  145. return false;
  146. }
  147. if( refvia->GetDrillValue() < dsnSettings.m_MicroViasMinDrill )
  148. {
  149. m_currentMarker = fillMarker( refvia, NULL,
  150. DRCE_TOO_SMALL_MICROVIA_DRILL, m_currentMarker );
  151. return false;
  152. }
  153. }
  154. else
  155. {
  156. if( refvia->GetWidth() < dsnSettings.m_ViasMinSize )
  157. {
  158. m_currentMarker = fillMarker( refvia, NULL,
  159. DRCE_TOO_SMALL_VIA, m_currentMarker );
  160. return false;
  161. }
  162. if( refvia->GetDrillValue() < dsnSettings.m_ViasMinDrill )
  163. {
  164. m_currentMarker = fillMarker( refvia, NULL,
  165. DRCE_TOO_SMALL_VIA_DRILL, m_currentMarker );
  166. return false;
  167. }
  168. }
  169. // test if via's hole is bigger than its diameter
  170. // This test is necessary since the via hole size and width can be modified
  171. // and a default via hole can be bigger than some vias sizes
  172. if( refvia->GetDrillValue() > refvia->GetWidth() )
  173. {
  174. m_currentMarker = fillMarker( refvia, NULL,
  175. DRCE_VIA_HOLE_BIGGER, m_currentMarker );
  176. return false;
  177. }
  178. // test if the type of via is allowed due to design rules
  179. if( ( refvia->GetViaType() == VIA_MICROVIA ) &&
  180. ( m_pcb->GetDesignSettings().m_MicroViasAllowed == false ) )
  181. {
  182. m_currentMarker = fillMarker( refvia, NULL,
  183. DRCE_MICRO_VIA_NOT_ALLOWED, m_currentMarker );
  184. return false;
  185. }
  186. // test if the type of via is allowed due to design rules
  187. if( ( refvia->GetViaType() == VIA_BLIND_BURIED ) &&
  188. ( m_pcb->GetDesignSettings().m_BlindBuriedViaAllowed == false ) )
  189. {
  190. m_currentMarker = fillMarker( refvia, NULL,
  191. DRCE_BURIED_VIA_NOT_ALLOWED, m_currentMarker );
  192. return false;
  193. }
  194. // For microvias: test if they are blind vias and only between 2 layers
  195. // because they are used for very small drill size and are drill by laser
  196. // and **only one layer** can be drilled
  197. if( refvia->GetViaType() == VIA_MICROVIA )
  198. {
  199. PCB_LAYER_ID layer1, layer2;
  200. bool err = true;
  201. refvia->LayerPair( &layer1, &layer2 );
  202. if( layer1 > layer2 )
  203. std::swap( layer1, layer2 );
  204. if( layer2 == B_Cu && layer1 == m_pcb->GetDesignSettings().GetCopperLayerCount() - 2 )
  205. err = false;
  206. else if( layer1 == F_Cu && layer2 == In1_Cu )
  207. err = false;
  208. if( err )
  209. {
  210. m_currentMarker = fillMarker( refvia, NULL,
  211. DRCE_MICRO_VIA_INCORRECT_LAYER_PAIR, m_currentMarker );
  212. return false;
  213. }
  214. }
  215. }
  216. else // This is a track segment
  217. {
  218. if( aRefSeg->GetWidth() < dsnSettings.m_TrackMinWidth )
  219. {
  220. m_currentMarker = fillMarker( aRefSeg, NULL,
  221. DRCE_TOO_SMALL_TRACK_WIDTH, m_currentMarker );
  222. return false;
  223. }
  224. }
  225. // for a non horizontal or vertical segment Compute the segment angle
  226. // in tenths of degrees and its length
  227. if( delta.x || delta.y )
  228. {
  229. // Compute the segment angle in 0,1 degrees
  230. m_segmAngle = ArcTangente( delta.y, delta.x );
  231. // Compute the segment length: we build an equivalent rotated segment,
  232. // this segment is horizontal, therefore dx = length
  233. RotatePoint( &delta, m_segmAngle ); // delta.x = length, delta.y = 0
  234. }
  235. m_segmLength = delta.x;
  236. /******************************************/
  237. /* Phase 1 : test DRC track to pads : */
  238. /******************************************/
  239. /* Use a dummy pad to test DRC tracks versus holes, for pads not on all copper layers
  240. * but having a hole
  241. * This dummy pad has the size and shape of the hole
  242. * to test tracks to pad hole DRC, using checkClearanceSegmToPad test function.
  243. * Therefore, this dummy pad is a circle or an oval.
  244. * A pad must have a parent because some functions expect a non null parent
  245. * to find the parent board, and some other data
  246. */
  247. MODULE dummymodule( m_pcb ); // Creates a dummy parent
  248. D_PAD dummypad( &dummymodule );
  249. dummypad.SetLayerSet( LSET::AllCuMask() ); // Ensure the hole is on all layers
  250. // Compute the min distance to pads
  251. if( testPads )
  252. {
  253. unsigned pad_count = m_pcb->GetPadCount();
  254. auto pads = m_pcb->GetPads();
  255. for( unsigned ii = 0; ii<pad_count; ++ii )
  256. {
  257. D_PAD* pad = pads[ii];
  258. /* No problem if pads are on an other layer,
  259. * But if a drill hole exists (a pad on a single layer can have a hole!)
  260. * we must test the hole
  261. */
  262. if( !( pad->GetLayerSet() & layerMask ).any() )
  263. {
  264. /* We must test the pad hole. In order to use the function
  265. * checkClearanceSegmToPad(),a pseudo pad is used, with a shape and a
  266. * size like the hole
  267. */
  268. if( pad->GetDrillSize().x == 0 )
  269. continue;
  270. dummypad.SetSize( pad->GetDrillSize() );
  271. dummypad.SetPosition( pad->GetPosition() );
  272. dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ?
  273. PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE );
  274. dummypad.SetOrientation( pad->GetOrientation() );
  275. m_padToTestPos = dummypad.GetPosition() - origin;
  276. if( !checkClearanceSegmToPad( &dummypad, aRefSeg->GetWidth(),
  277. netclass->GetClearance() ) )
  278. {
  279. m_currentMarker = fillMarker( aRefSeg, pad,
  280. DRCE_TRACK_NEAR_THROUGH_HOLE, m_currentMarker );
  281. return false;
  282. }
  283. continue;
  284. }
  285. // The pad must be in a net (i.e pt_pad->GetNet() != 0 )
  286. // but no problem if the pad netcode is the current netcode (same net)
  287. if( pad->GetNetCode() // the pad must be connected
  288. && net_code_ref == pad->GetNetCode() ) // the pad net is the same as current net -> Ok
  289. continue;
  290. // DRC for the pad
  291. shape_pos = pad->ShapePos();
  292. m_padToTestPos = shape_pos - origin;
  293. if( !checkClearanceSegmToPad( pad, aRefSeg->GetWidth(), aRefSeg->GetClearance( pad ) ) )
  294. {
  295. m_currentMarker = fillMarker( aRefSeg, pad,
  296. DRCE_TRACK_NEAR_PAD, m_currentMarker );
  297. return false;
  298. }
  299. }
  300. }
  301. /***********************************************/
  302. /* Phase 2: test DRC with other track segments */
  303. /***********************************************/
  304. // At this point the reference segment is the X axis
  305. // Test the reference segment with other track segments
  306. wxPoint segStartPoint;
  307. wxPoint segEndPoint;
  308. for( track = aStart; track; track = track->Next() )
  309. {
  310. // No problem if segments have the same net code:
  311. if( net_code_ref == track->GetNetCode() )
  312. continue;
  313. // No problem if segment are on different layers :
  314. if( !( layerMask & track->GetLayerSet() ).any() )
  315. continue;
  316. // the minimum distance = clearance plus half the reference track
  317. // width plus half the other track's width
  318. int w_dist = aRefSeg->GetClearance( track );
  319. w_dist += (aRefSeg->GetWidth() + track->GetWidth()) / 2;
  320. // Due to many double to int conversions during calculations, which
  321. // create rounding issues,
  322. // the exact clearance margin cannot be really known.
  323. // To avoid false bad DRC detection due to these rounding issues,
  324. // slightly decrease the w_dist (remove one nanometer is enough !)
  325. w_dist -= 1;
  326. // If the reference segment is a via, we test it here
  327. if( aRefSeg->Type() == PCB_VIA_T )
  328. {
  329. delta = track->GetEnd() - track->GetStart();
  330. segStartPoint = aRefSeg->GetStart() - track->GetStart();
  331. if( track->Type() == PCB_VIA_T )
  332. {
  333. // Test distance between two vias, i.e. two circles, trivial case
  334. if( EuclideanNorm( segStartPoint ) < w_dist )
  335. {
  336. m_currentMarker = fillMarker( aRefSeg, track,
  337. DRCE_VIA_NEAR_VIA, m_currentMarker );
  338. return false;
  339. }
  340. }
  341. else // test via to segment
  342. {
  343. // Compute l'angle du segment a tester;
  344. double angle = ArcTangente( delta.y, delta.x );
  345. // Compute new coordinates ( the segment become horizontal)
  346. RotatePoint( &delta, angle );
  347. RotatePoint( &segStartPoint, angle );
  348. if( !checkMarginToCircle( segStartPoint, w_dist, delta.x ) )
  349. {
  350. m_currentMarker = fillMarker( track, aRefSeg,
  351. DRCE_VIA_NEAR_TRACK, m_currentMarker );
  352. return false;
  353. }
  354. }
  355. continue;
  356. }
  357. /* We compute segStartPoint, segEndPoint = starting and ending point coordinates for
  358. * the segment to test in the new axis : the new X axis is the
  359. * reference segment. We must translate and rotate the segment to test
  360. */
  361. segStartPoint = track->GetStart() - origin;
  362. segEndPoint = track->GetEnd() - origin;
  363. RotatePoint( &segStartPoint, m_segmAngle );
  364. RotatePoint( &segEndPoint, m_segmAngle );
  365. if( track->Type() == PCB_VIA_T )
  366. {
  367. if( checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
  368. continue;
  369. m_currentMarker = fillMarker( aRefSeg, track,
  370. DRCE_TRACK_NEAR_VIA, m_currentMarker );
  371. return false;
  372. }
  373. /* We have changed axis:
  374. * the reference segment is Horizontal.
  375. * 3 cases : the segment to test can be parallel, perpendicular or have an other direction
  376. */
  377. if( segStartPoint.y == segEndPoint.y ) // parallel segments
  378. {
  379. if( abs( segStartPoint.y ) >= w_dist )
  380. continue;
  381. // Ensure segStartPoint.x <= segEndPoint.x
  382. if( segStartPoint.x > segEndPoint.x )
  383. std::swap( segStartPoint.x, segEndPoint.x );
  384. if( segStartPoint.x > (-w_dist) && segStartPoint.x < (m_segmLength + w_dist) ) /* possible error drc */
  385. {
  386. // the start point is inside the reference range
  387. // X........
  388. // O--REF--+
  389. // Fine test : we consider the rounded shape of each end of the track segment:
  390. if( segStartPoint.x >= 0 && segStartPoint.x <= m_segmLength )
  391. {
  392. m_currentMarker = fillMarker( aRefSeg, track,
  393. DRCE_TRACK_ENDS1, m_currentMarker );
  394. return false;
  395. }
  396. if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
  397. {
  398. m_currentMarker = fillMarker( aRefSeg, track,
  399. DRCE_TRACK_ENDS2, m_currentMarker );
  400. return false;
  401. }
  402. }
  403. if( segEndPoint.x > (-w_dist) && segEndPoint.x < (m_segmLength + w_dist) )
  404. {
  405. // the end point is inside the reference range
  406. // .....X
  407. // O--REF--+
  408. // Fine test : we consider the rounded shape of the ends
  409. if( segEndPoint.x >= 0 && segEndPoint.x <= m_segmLength )
  410. {
  411. m_currentMarker = fillMarker( aRefSeg, track,
  412. DRCE_TRACK_ENDS3, m_currentMarker );
  413. return false;
  414. }
  415. if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) )
  416. {
  417. m_currentMarker = fillMarker( aRefSeg, track,
  418. DRCE_TRACK_ENDS4, m_currentMarker );
  419. return false;
  420. }
  421. }
  422. if( segStartPoint.x <=0 && segEndPoint.x >= 0 )
  423. {
  424. // the segment straddles the reference range (this actually only
  425. // checks if it straddles the origin, because the other cases where already
  426. // handled)
  427. // X.............X
  428. // O--REF--+
  429. m_currentMarker = fillMarker( aRefSeg, track,
  430. DRCE_TRACK_SEGMENTS_TOO_CLOSE, m_currentMarker );
  431. return false;
  432. }
  433. }
  434. else if( segStartPoint.x == segEndPoint.x ) // perpendicular segments
  435. {
  436. if( ( segStartPoint.x <= (-w_dist) ) || ( segStartPoint.x >= (m_segmLength + w_dist) ) )
  437. continue;
  438. // Test if segments are crossing
  439. if( segStartPoint.y > segEndPoint.y )
  440. std::swap( segStartPoint.y, segEndPoint.y );
  441. if( (segStartPoint.y < 0) && (segEndPoint.y > 0) )
  442. {
  443. m_currentMarker = fillMarker( aRefSeg, track,
  444. DRCE_TRACKS_CROSSING, m_currentMarker );
  445. return false;
  446. }
  447. // At this point the drc error is due to an end near a reference segm end
  448. if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
  449. {
  450. m_currentMarker = fillMarker( aRefSeg, track,
  451. DRCE_ENDS_PROBLEM1, m_currentMarker );
  452. return false;
  453. }
  454. if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) )
  455. {
  456. m_currentMarker = fillMarker( aRefSeg, track,
  457. DRCE_ENDS_PROBLEM2, m_currentMarker );
  458. return false;
  459. }
  460. }
  461. else // segments quelconques entre eux
  462. {
  463. // calcul de la "surface de securite du segment de reference
  464. // First rought 'and fast) test : the track segment is like a rectangle
  465. m_xcliplo = m_ycliplo = -w_dist;
  466. m_xcliphi = m_segmLength + w_dist;
  467. m_ycliphi = w_dist;
  468. // A fine test is needed because a serment is not exactly a
  469. // rectangle, it has rounded ends
  470. if( !checkLine( segStartPoint, segEndPoint ) )
  471. {
  472. /* 2eme passe : the track has rounded ends.
  473. * we must a fine test for each rounded end and the
  474. * rectangular zone
  475. */
  476. m_xcliplo = 0;
  477. m_xcliphi = m_segmLength;
  478. if( !checkLine( segStartPoint, segEndPoint ) )
  479. {
  480. m_currentMarker = fillMarker( aRefSeg, track,
  481. DRCE_ENDS_PROBLEM3, m_currentMarker );
  482. return false;
  483. }
  484. else // The drc error is due to the starting or the ending point of the reference segment
  485. {
  486. // Test the starting and the ending point
  487. segStartPoint = track->GetStart();
  488. segEndPoint = track->GetEnd();
  489. delta = segEndPoint - segStartPoint;
  490. // Compute the segment orientation (angle) en 0,1 degre
  491. double angle = ArcTangente( delta.y, delta.x );
  492. // Compute the segment length: delta.x = length after rotation
  493. RotatePoint( &delta, angle );
  494. /* Comute the reference segment coordinates relatives to a
  495. * X axis = current tested segment
  496. */
  497. wxPoint relStartPos = aRefSeg->GetStart() - segStartPoint;
  498. wxPoint relEndPos = aRefSeg->GetEnd() - segStartPoint;
  499. RotatePoint( &relStartPos, angle );
  500. RotatePoint( &relEndPos, angle );
  501. if( !checkMarginToCircle( relStartPos, w_dist, delta.x ) )
  502. {
  503. m_currentMarker = fillMarker( aRefSeg, track,
  504. DRCE_ENDS_PROBLEM4, m_currentMarker );
  505. return false;
  506. }
  507. if( !checkMarginToCircle( relEndPos, w_dist, delta.x ) )
  508. {
  509. m_currentMarker = fillMarker( aRefSeg, track,
  510. DRCE_ENDS_PROBLEM5, m_currentMarker );
  511. return false;
  512. }
  513. }
  514. }
  515. }
  516. }
  517. return true;
  518. }
  519. bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex )
  520. {
  521. if( !aArea->IsOnCopperLayer() ) // Cannot have a Drc error if not on copper layer
  522. return true;
  523. // Get polygon, contour and vertex index.
  524. SHAPE_POLY_SET::VERTEX_INDEX index;
  525. // If the vertex does not exist, there is no conflict
  526. if( !aArea->Outline()->GetRelativeIndices( aCornerIndex, &index ) )
  527. return true;
  528. // Retrieve the selected contour
  529. SHAPE_LINE_CHAIN contour;
  530. contour = aArea->Outline()->Polygon( index.m_polygon )[index.m_contour];
  531. // Retrieve the segment that starts at aCornerIndex-th corner.
  532. SEG selectedSegment = contour.Segment( index.m_vertex );
  533. VECTOR2I start = selectedSegment.A;
  534. VECTOR2I end = selectedSegment.B;
  535. // iterate through all areas
  536. for( int ia2 = 0; ia2 < m_pcb->GetAreaCount(); ia2++ )
  537. {
  538. ZONE_CONTAINER* area_to_test = m_pcb->GetArea( ia2 );
  539. int zone_clearance = std::max( area_to_test->GetZoneClearance(),
  540. aArea->GetZoneClearance() );
  541. // test for same layer
  542. if( area_to_test->GetLayer() != aArea->GetLayer() )
  543. continue;
  544. // Test for same net
  545. if( ( aArea->GetNetCode() == area_to_test->GetNetCode() ) && (aArea->GetNetCode() >= 0) )
  546. continue;
  547. // test for same priority
  548. if( area_to_test->GetPriority() != aArea->GetPriority() )
  549. continue;
  550. // test for same type
  551. if( area_to_test->GetIsKeepout() != aArea->GetIsKeepout() )
  552. continue;
  553. // For keepout, there is no clearance, so use a minimal value for it
  554. // use 1, not 0 as value to avoid some issues in tests
  555. if( area_to_test->GetIsKeepout() )
  556. zone_clearance = 1;
  557. // test for ending line inside area_to_test
  558. if( area_to_test->Outline()->Contains( end ) )
  559. {
  560. // COPPERAREA_COPPERAREA error: corner inside copper area
  561. m_currentMarker = fillMarker( aArea, static_cast<wxPoint>( end ),
  562. COPPERAREA_INSIDE_COPPERAREA,
  563. m_currentMarker );
  564. return false;
  565. }
  566. // now test spacing between areas
  567. int ax1 = start.x;
  568. int ay1 = start.y;
  569. int ax2 = end.x;
  570. int ay2 = end.y;
  571. // Iterate through all edges in the polygon.
  572. SHAPE_POLY_SET::SEGMENT_ITERATOR iterator;
  573. for( iterator = area_to_test->Outline()->IterateSegmentsWithHoles(); iterator; iterator++ )
  574. {
  575. SEG segment = *iterator;
  576. int bx1 = segment.A.x;
  577. int by1 = segment.A.y;
  578. int bx2 = segment.B.x;
  579. int by2 = segment.B.y;
  580. int x, y; // variables containing the intersecting point coordinates
  581. int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2,
  582. 0,
  583. ax1, ay1, ax2, ay2,
  584. 0,
  585. zone_clearance,
  586. &x, &y );
  587. if( d < zone_clearance )
  588. {
  589. // COPPERAREA_COPPERAREA error : edge intersect or too close
  590. m_currentMarker = fillMarker( aArea, wxPoint( x, y ),
  591. COPPERAREA_CLOSE_TO_COPPERAREA,
  592. m_currentMarker );
  593. return false;
  594. }
  595. }
  596. }
  597. return true;
  598. }
  599. bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
  600. {
  601. int dist;
  602. double pad_angle;
  603. // Get the clearance between the 2 pads. this is the min distance between aRefPad and aPad
  604. int dist_min = aRefPad->GetClearance( aPad );
  605. // relativePadPos is the aPad shape position relative to the aRefPad shape position
  606. wxPoint relativePadPos = aPad->ShapePos() - aRefPad->ShapePos();
  607. dist = KiROUND( EuclideanNorm( relativePadPos ) );
  608. // Quick test: Clearance is OK if the bounding circles are further away than "dist_min"
  609. int delta = dist - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius();
  610. if( delta >= dist_min )
  611. return true;
  612. /* Here, pads are near and DRC depend on the pad shapes
  613. * We must compare distance using a fine shape analysis
  614. * Because a circle or oval shape is the easier shape to test, try to have
  615. * aRefPad shape type = PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL.
  616. * if aRefPad = TRAP. and aPad = RECT, also swap pads
  617. * Swap aRefPad and aPad if needed
  618. */
  619. bool swap_pads;
  620. swap_pads = false;
  621. // swap pads to make comparisons easier
  622. // Note also a ROUNDRECT pad with a corner radius = r can be considered as
  623. // a smaller RECT (size - 2*r) with a clearance increased by r
  624. // priority is aRefPad = ROUND then OVAL then RECT/ROUNDRECT then other
  625. if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_SHAPE_CIRCLE )
  626. {
  627. // pad ref shape is here oval, rect, roundrect, trapezoid or custom
  628. switch( aPad->GetShape() )
  629. {
  630. case PAD_SHAPE_CIRCLE:
  631. swap_pads = true;
  632. break;
  633. case PAD_SHAPE_OVAL:
  634. swap_pads = true;
  635. break;
  636. case PAD_SHAPE_RECT:
  637. case PAD_SHAPE_ROUNDRECT:
  638. if( aRefPad->GetShape() != PAD_SHAPE_OVAL )
  639. swap_pads = true;
  640. break;
  641. case PAD_SHAPE_TRAPEZOID:
  642. case PAD_SHAPE_CUSTOM:
  643. break;
  644. }
  645. }
  646. if( swap_pads )
  647. {
  648. std::swap( aRefPad, aPad );
  649. relativePadPos = -relativePadPos;
  650. }
  651. // corners of aRefPad (used only for rect/roundrect/trap pad)
  652. wxPoint polyref[4];
  653. // corners of aRefPad (used only for custom pad)
  654. SHAPE_POLY_SET polysetref;
  655. // corners of aPad (used only for rect/roundrect/trap pad)
  656. wxPoint polycompare[4];
  657. // corners of aPad (used only custom pad)
  658. SHAPE_POLY_SET polysetcompare;
  659. /* Because pad exchange, aRefPad shape is PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL,
  660. * if one of the 2 pads was a PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL.
  661. * Therefore, if aRefPad is a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID,
  662. * aPad is also a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID
  663. */
  664. bool diag = true;
  665. switch( aRefPad->GetShape() )
  666. {
  667. case PAD_SHAPE_CIRCLE:
  668. /* One can use checkClearanceSegmToPad to test clearance
  669. * aRefPad is like a track segment with a null length and a witdth = GetSize().x
  670. */
  671. m_segmLength = 0;
  672. m_segmAngle = 0;
  673. m_segmEnd.x = m_segmEnd.y = 0;
  674. m_padToTestPos = relativePadPos;
  675. diag = checkClearanceSegmToPad( aPad, aRefPad->GetSize().x, dist_min );
  676. break;
  677. case PAD_SHAPE_TRAPEZOID:
  678. case PAD_SHAPE_ROUNDRECT:
  679. case PAD_SHAPE_RECT:
  680. case PAD_SHAPE_CUSTOM:
  681. // pad_angle = pad orient relative to the aRefPad orient
  682. pad_angle = aRefPad->GetOrientation() + aPad->GetOrientation();
  683. NORMALIZE_ANGLE_POS( pad_angle );
  684. if( aRefPad->GetShape() == PAD_SHAPE_ROUNDRECT )
  685. {
  686. int padRadius = aRefPad->GetRoundRectCornerRadius();
  687. dist_min += padRadius;
  688. GetRoundRectCornerCenters( polyref, padRadius, wxPoint( 0, 0 ),
  689. aRefPad->GetSize(), aRefPad->GetOrientation() );
  690. }
  691. else if( aRefPad->GetShape() == PAD_SHAPE_CUSTOM )
  692. {
  693. polysetref.Append( aRefPad->GetCustomShapeAsPolygon() );
  694. // The reference pad can be rotated. calculate the rotated
  695. // coordiantes ( note, the ref pad position is the origin of
  696. // coordinates for this drc test)
  697. aRefPad->CustomShapeAsPolygonToBoardPosition( &polysetref,
  698. wxPoint( 0, 0 ), aRefPad->GetOrientation() );
  699. }
  700. else
  701. {
  702. // BuildPadPolygon has meaning for rect a trapeziod shapes
  703. // and returns the 4 corners
  704. aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() );
  705. }
  706. switch( aPad->GetShape() )
  707. {
  708. case PAD_SHAPE_ROUNDRECT:
  709. case PAD_SHAPE_RECT:
  710. case PAD_SHAPE_TRAPEZOID:
  711. case PAD_SHAPE_CUSTOM:
  712. if( aPad->GetShape() == PAD_SHAPE_ROUNDRECT )
  713. {
  714. int padRadius = aPad->GetRoundRectCornerRadius();
  715. dist_min += padRadius;
  716. GetRoundRectCornerCenters( polycompare, padRadius, relativePadPos,
  717. aPad->GetSize(), aPad->GetOrientation() );
  718. }
  719. else if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
  720. {
  721. polysetcompare.Append( aPad->GetCustomShapeAsPolygon() );
  722. // The pad to compare can be rotated. calculate the rotated
  723. // coordinattes ( note, the pad to compare position
  724. // is the relativePadPos for this drc test
  725. aPad->CustomShapeAsPolygonToBoardPosition( &polysetcompare,
  726. relativePadPos, aPad->GetOrientation() );
  727. }
  728. else
  729. {
  730. aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() );
  731. // Move aPad shape to relativePadPos
  732. for( int ii = 0; ii < 4; ii++ )
  733. polycompare[ii] += relativePadPos;
  734. }
  735. // And now test polygons: We have 3 cases:
  736. // one poly is complex and the other is basic (has only 4 corners)
  737. // both polys are complex
  738. // both polys are basic (have only 4 corners) the most usual case
  739. if( polysetref.OutlineCount() && polysetcompare.OutlineCount() == 0)
  740. {
  741. const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
  742. // And now test polygons:
  743. if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
  744. polycompare, 4, dist_min ) )
  745. diag = false;
  746. }
  747. else if( polysetref.OutlineCount() == 0 && polysetcompare.OutlineCount())
  748. {
  749. const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 );
  750. // And now test polygons:
  751. if( !poly2polyDRC( (wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(),
  752. polyref, 4, dist_min ) )
  753. diag = false;
  754. }
  755. else if( polysetref.OutlineCount() && polysetcompare.OutlineCount() )
  756. {
  757. const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
  758. const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 );
  759. // And now test polygons:
  760. if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
  761. (wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(), dist_min ) )
  762. diag = false;
  763. }
  764. else if( !poly2polyDRC( polyref, 4, polycompare, 4, dist_min ) )
  765. diag = false;
  766. break;
  767. default:
  768. wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape %d" ), aPad->GetShape() );
  769. break;
  770. }
  771. break;
  772. case PAD_SHAPE_OVAL: /* an oval pad is like a track segment */
  773. {
  774. /* Create a track segment with same dimensions as the oval aRefPad
  775. * and use checkClearanceSegmToPad function to test aPad to aRefPad clearance
  776. */
  777. int segm_width;
  778. m_segmAngle = aRefPad->GetOrientation(); // Segment orient.
  779. if( aRefPad->GetSize().y < aRefPad->GetSize().x ) // Build an horizontal equiv segment
  780. {
  781. segm_width = aRefPad->GetSize().y;
  782. m_segmLength = aRefPad->GetSize().x - aRefPad->GetSize().y;
  783. }
  784. else // Vertical oval: build an horizontal equiv segment and rotate 90.0 deg
  785. {
  786. segm_width = aRefPad->GetSize().x;
  787. m_segmLength = aRefPad->GetSize().y - aRefPad->GetSize().x;
  788. m_segmAngle += 900;
  789. }
  790. /* the start point must be 0,0 and currently relativePadPos
  791. * is relative the center of pad coordinate */
  792. wxPoint segstart;
  793. segstart.x = -m_segmLength / 2; // Start point coordinate of the horizontal equivalent segment
  794. RotatePoint( &segstart, m_segmAngle ); // actual start point coordinate of the equivalent segment
  795. // Calculate segment end position relative to the segment origin
  796. m_segmEnd.x = -2 * segstart.x;
  797. m_segmEnd.y = -2 * segstart.y;
  798. // Recalculate the equivalent segment angle in 0,1 degrees
  799. // to prepare a call to checkClearanceSegmToPad()
  800. m_segmAngle = ArcTangente( m_segmEnd.y, m_segmEnd.x );
  801. // move pad position relative to the segment origin
  802. m_padToTestPos = relativePadPos - segstart;
  803. // Use segment to pad check to test the second pad:
  804. diag = checkClearanceSegmToPad( aPad, segm_width, dist_min );
  805. break;
  806. }
  807. default:
  808. wxMessageBox( wxT( "DRC::checkClearancePadToPad: unknown pad shape" ) );
  809. break;
  810. }
  811. return diag;
  812. }
  813. /* test if distance between a segment is > aMinDist
  814. * segment start point is assumed in (0,0) and segment start point in m_segmEnd
  815. * and its orientation is m_segmAngle (m_segmAngle must be already initialized)
  816. * and have aSegmentWidth.
  817. */
  818. bool DRC::checkClearanceSegmToPad( const D_PAD* aPad, int aSegmentWidth, int aMinDist )
  819. {
  820. // Note:
  821. // we are using a horizontal segment for test, because we know here
  822. // only the length and orientation+ of the segment
  823. // Therefore the coordinates of the shape of pad to compare
  824. // must be calculated in a axis system rotated by m_segmAngle
  825. // and centered to the segment origin, before they can be tested
  826. // against the segment
  827. // We are using:
  828. // m_padToTestPos the position of the pad shape in this axis system
  829. // m_segmAngle the axis system rotation
  830. int segmHalfWidth = aSegmentWidth / 2;
  831. int distToLine = segmHalfWidth + aMinDist;
  832. wxSize padHalfsize; // half dimension of the pad
  833. if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
  834. {
  835. // For a custom pad, the pad size has no meaning, we only can
  836. // use the bounding radius
  837. padHalfsize.x = padHalfsize.y = aPad->GetBoundingRadius();
  838. }
  839. else
  840. {
  841. padHalfsize.x = aPad->GetSize().x >> 1;
  842. padHalfsize.y = aPad->GetSize().y >> 1;
  843. }
  844. if( aPad->GetShape() == PAD_SHAPE_TRAPEZOID ) // The size is bigger, due to GetDelta() extra size
  845. {
  846. padHalfsize.x += std::abs(aPad->GetDelta().y) / 2; // Remember: GetDelta().y is the GetSize().x change
  847. padHalfsize.y += std::abs(aPad->GetDelta().x) / 2; // Remember: GetDelta().x is the GetSize().y change
  848. }
  849. if( aPad->GetShape() == PAD_SHAPE_CIRCLE )
  850. {
  851. /* Easy case: just test the distance between segment and pad centre
  852. * calculate pad coordinates in the X,Y axis with X axis = segment to test
  853. */
  854. RotatePoint( &m_padToTestPos, m_segmAngle );
  855. return checkMarginToCircle( m_padToTestPos, distToLine + padHalfsize.x, m_segmLength );
  856. }
  857. /* calculate the bounding box of the pad, including the clearance and the segment width
  858. * if the line from 0 to m_segmEnd does not intersect this bounding box,
  859. * the clearance is always OK
  860. * But if intersect, a better analysis of the pad shape must be done.
  861. */
  862. m_xcliplo = m_padToTestPos.x - distToLine - padHalfsize.x;
  863. m_ycliplo = m_padToTestPos.y - distToLine - padHalfsize.y;
  864. m_xcliphi = m_padToTestPos.x + distToLine + padHalfsize.x;
  865. m_ycliphi = m_padToTestPos.y + distToLine + padHalfsize.y;
  866. wxPoint startPoint;
  867. wxPoint endPoint = m_segmEnd;
  868. double orient = aPad->GetOrientation();
  869. RotatePoint( &startPoint, m_padToTestPos, -orient );
  870. RotatePoint( &endPoint, m_padToTestPos, -orient );
  871. if( checkLine( startPoint, endPoint ) )
  872. return true;
  873. /* segment intersects the bounding box. But there is not always a DRC error.
  874. * A fine analysis of the pad shape must be done.
  875. */
  876. switch( aPad->GetShape() )
  877. {
  878. case PAD_SHAPE_CIRCLE:
  879. // This case was already tested, so it cannot be found here.
  880. // it is here just to avoid compil warning, and to remember
  881. // it is already tested.
  882. return false;
  883. case PAD_SHAPE_OVAL:
  884. {
  885. /* an oval is a complex shape, but is a rectangle and 2 circles
  886. * these 3 basic shapes are more easy to test.
  887. *
  888. * In calculations we are using a vertical or horizontal oval shape
  889. * (i.e. a vertical or horizontal rounded segment)
  890. */
  891. wxPoint cstart = m_padToTestPos;
  892. wxPoint cend = m_padToTestPos; // center of each circle
  893. int delta = std::abs( padHalfsize.y - padHalfsize.x );
  894. int radius = std::min( padHalfsize.y, padHalfsize.x );
  895. if( padHalfsize.x > padHalfsize.y ) // horizontal equivalent segment
  896. {
  897. cstart.x -= delta;
  898. cend.x += delta;
  899. // Build the rectangular clearance area between the two circles
  900. // the rect starts at cstart.x and ends at cend.x and its height
  901. // is (radius + distToLine)*2
  902. m_xcliplo = cstart.x;
  903. m_ycliplo = cstart.y - radius - distToLine;
  904. m_xcliphi = cend.x;
  905. m_ycliphi = cend.y + radius + distToLine;
  906. }
  907. else // vertical equivalent segment
  908. {
  909. cstart.y -= delta;
  910. cend.y += delta;
  911. // Build the rectangular clearance area between the two circles
  912. // the rect starts at cstart.y and ends at cend.y and its width
  913. // is (radius + distToLine)*2
  914. m_xcliplo = cstart.x - distToLine - radius;
  915. m_ycliplo = cstart.y;
  916. m_xcliphi = cend.x + distToLine +radius;
  917. m_ycliphi = cend.y;
  918. }
  919. // Test the rectangular clearance area between the two circles (the rounded ends)
  920. if( !checkLine( startPoint, endPoint ) )
  921. {
  922. return false;
  923. }
  924. // test the first end
  925. // Calculate the actual position of the circle, given the pad orientation:
  926. RotatePoint( &cstart, m_padToTestPos, orient );
  927. // Calculate the actual position of the circle in the new X,Y axis, relative
  928. // to the segment:
  929. RotatePoint( &cstart, m_segmAngle );
  930. if( !checkMarginToCircle( cstart, radius + distToLine, m_segmLength ) )
  931. {
  932. return false;
  933. }
  934. // test the second end
  935. RotatePoint( &cend, m_padToTestPos, orient );
  936. RotatePoint( &cend, m_segmAngle );
  937. if( !checkMarginToCircle( cend, radius + distToLine, m_segmLength ) )
  938. {
  939. return false;
  940. }
  941. }
  942. break;
  943. case PAD_SHAPE_ROUNDRECT:
  944. {
  945. // a round rect is a smaller rect, with a clearance augmented by the corners radius
  946. int r = aPad->GetRoundRectCornerRadius();
  947. padHalfsize.x -= r;
  948. padHalfsize.y -= r;
  949. distToLine += r;
  950. }
  951. // Fall through
  952. case PAD_SHAPE_RECT:
  953. // the area to test is a rounded rectangle.
  954. // this can be done by testing 2 rectangles and 4 circles (the corners)
  955. // Testing the first rectangle dimx + distToLine, dimy:
  956. m_xcliplo = m_padToTestPos.x - padHalfsize.x - distToLine;
  957. m_ycliplo = m_padToTestPos.y - padHalfsize.y;
  958. m_xcliphi = m_padToTestPos.x + padHalfsize.x + distToLine;
  959. m_ycliphi = m_padToTestPos.y + padHalfsize.y;
  960. if( !checkLine( startPoint, endPoint ) )
  961. return false;
  962. // Testing the second rectangle dimx , dimy + distToLine
  963. m_xcliplo = m_padToTestPos.x - padHalfsize.x;
  964. m_ycliplo = m_padToTestPos.y - padHalfsize.y - distToLine;
  965. m_xcliphi = m_padToTestPos.x + padHalfsize.x;
  966. m_ycliphi = m_padToTestPos.y + padHalfsize.y + distToLine;
  967. if( !checkLine( startPoint, endPoint ) )
  968. return false;
  969. // testing the 4 circles which are the clearance area of each corner:
  970. // testing the left top corner of the rectangle
  971. startPoint.x = m_padToTestPos.x - padHalfsize.x;
  972. startPoint.y = m_padToTestPos.y - padHalfsize.y;
  973. RotatePoint( &startPoint, m_padToTestPos, orient );
  974. RotatePoint( &startPoint, m_segmAngle );
  975. if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
  976. return false;
  977. // testing the right top corner of the rectangle
  978. startPoint.x = m_padToTestPos.x + padHalfsize.x;
  979. startPoint.y = m_padToTestPos.y - padHalfsize.y;
  980. RotatePoint( &startPoint, m_padToTestPos, orient );
  981. RotatePoint( &startPoint, m_segmAngle );
  982. if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
  983. return false;
  984. // testing the left bottom corner of the rectangle
  985. startPoint.x = m_padToTestPos.x - padHalfsize.x;
  986. startPoint.y = m_padToTestPos.y + padHalfsize.y;
  987. RotatePoint( &startPoint, m_padToTestPos, orient );
  988. RotatePoint( &startPoint, m_segmAngle );
  989. if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
  990. return false;
  991. // testing the right bottom corner of the rectangle
  992. startPoint.x = m_padToTestPos.x + padHalfsize.x;
  993. startPoint.y = m_padToTestPos.y + padHalfsize.y;
  994. RotatePoint( &startPoint, m_padToTestPos, orient );
  995. RotatePoint( &startPoint, m_segmAngle );
  996. if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
  997. return false;
  998. break;
  999. case PAD_SHAPE_TRAPEZOID:
  1000. {
  1001. wxPoint poly[4];
  1002. aPad->BuildPadPolygon( poly, wxSize( 0, 0 ), orient );
  1003. // Move shape to m_padToTestPos
  1004. for( int ii = 0; ii < 4; ii++ )
  1005. {
  1006. poly[ii] += m_padToTestPos;
  1007. RotatePoint( &poly[ii], m_segmAngle );
  1008. }
  1009. if( !poly2segmentDRC( poly, 4, wxPoint( 0, 0 ),
  1010. wxPoint(m_segmLength,0), distToLine ) )
  1011. return false;
  1012. }
  1013. break;
  1014. case PAD_SHAPE_CUSTOM:
  1015. {
  1016. SHAPE_POLY_SET polyset;
  1017. polyset.Append( aPad->GetCustomShapeAsPolygon() );
  1018. // The pad can be rotated. calculate the coordinates
  1019. // relatives to the segment being tested
  1020. // Note, the pad position relative to the segment origin
  1021. // is m_padToTestPos
  1022. aPad->CustomShapeAsPolygonToBoardPosition( &polyset,
  1023. m_padToTestPos, orient );
  1024. // Rotate all coordinates by m_segmAngle, because the segment orient
  1025. // is m_segmAngle
  1026. // we are using a horizontal segment for test, because we know here
  1027. // only the lenght and orientation+ of the segment
  1028. // therefore all coordinates of the pad to test must be rotated by
  1029. // m_segmAngle (they are already relative to the segment origin)
  1030. aPad->CustomShapeAsPolygonToBoardPosition( &polyset,
  1031. wxPoint( 0, 0 ), m_segmAngle );
  1032. const SHAPE_LINE_CHAIN& refpoly = polyset.COutline( 0 );
  1033. if( !poly2segmentDRC( (wxPoint*) &refpoly.CPoint( 0 ),
  1034. refpoly.PointCount(),
  1035. wxPoint( 0, 0 ), wxPoint(m_segmLength,0),
  1036. distToLine ) )
  1037. return false;
  1038. }
  1039. break;
  1040. }
  1041. return true;
  1042. }
  1043. /**
  1044. * Helper function checkMarginToCircle
  1045. * Check the distance between a circle (round pad, via or round end of track)
  1046. * and a segment. the segment is expected starting at 0,0, and on the X axis
  1047. * return true if distance >= aRadius
  1048. */
  1049. bool DRC::checkMarginToCircle( wxPoint aCentre, int aRadius, int aLength )
  1050. {
  1051. if( abs( aCentre.y ) >= aRadius ) // trivial case
  1052. return true;
  1053. // Here, distance between aCentre and X axis is < aRadius
  1054. if( (aCentre.x > -aRadius ) && ( aCentre.x < (aLength + aRadius) ) )
  1055. {
  1056. if( (aCentre.x >= 0) && (aCentre.x <= aLength) )
  1057. return false; // aCentre is between the starting point and the ending point of the segm
  1058. if( aCentre.x > aLength ) // aCentre is after the ending point
  1059. aCentre.x -= aLength; // move aCentre to the starting point of the segment
  1060. if( EuclideanNorm( aCentre ) < aRadius )
  1061. // distance between aCentre and the starting point or the ending point is < aRadius
  1062. return false;
  1063. }
  1064. return true;
  1065. }
  1066. // Helper function used in checkLine::
  1067. static inline int USCALE( unsigned arg, unsigned num, unsigned den )
  1068. {
  1069. int ii;
  1070. ii = KiROUND( ( (double) arg * num ) / den );
  1071. return ii;
  1072. }
  1073. /** Helper function checkLine
  1074. * Test if a line intersects a bounding box (a rectangle)
  1075. * The rectangle is defined by m_xcliplo, m_ycliplo and m_xcliphi, m_ycliphi
  1076. * return true if the line from aSegStart to aSegEnd is outside the bounding box
  1077. */
  1078. bool DRC::checkLine( wxPoint aSegStart, wxPoint aSegEnd )
  1079. {
  1080. #define WHEN_OUTSIDE return true
  1081. #define WHEN_INSIDE
  1082. int temp;
  1083. if( aSegStart.x > aSegEnd.x )
  1084. std::swap( aSegStart, aSegEnd );
  1085. if( (aSegEnd.x < m_xcliplo) || (aSegStart.x > m_xcliphi) )
  1086. {
  1087. WHEN_OUTSIDE;
  1088. }
  1089. if( aSegStart.y < aSegEnd.y )
  1090. {
  1091. if( (aSegEnd.y < m_ycliplo) || (aSegStart.y > m_ycliphi) )
  1092. {
  1093. WHEN_OUTSIDE;
  1094. }
  1095. if( aSegStart.y < m_ycliplo )
  1096. {
  1097. temp = USCALE( (aSegEnd.x - aSegStart.x), (m_ycliplo - aSegStart.y),
  1098. (aSegEnd.y - aSegStart.y) );
  1099. if( (aSegStart.x += temp) > m_xcliphi )
  1100. {
  1101. WHEN_OUTSIDE;
  1102. }
  1103. aSegStart.y = m_ycliplo;
  1104. WHEN_INSIDE;
  1105. }
  1106. if( aSegEnd.y > m_ycliphi )
  1107. {
  1108. temp = USCALE( (aSegEnd.x - aSegStart.x), (aSegEnd.y - m_ycliphi),
  1109. (aSegEnd.y - aSegStart.y) );
  1110. if( (aSegEnd.x -= temp) < m_xcliplo )
  1111. {
  1112. WHEN_OUTSIDE;
  1113. }
  1114. aSegEnd.y = m_ycliphi;
  1115. WHEN_INSIDE;
  1116. }
  1117. if( aSegStart.x < m_xcliplo )
  1118. {
  1119. temp = USCALE( (aSegEnd.y - aSegStart.y), (m_xcliplo - aSegStart.x),
  1120. (aSegEnd.x - aSegStart.x) );
  1121. aSegStart.y += temp;
  1122. aSegStart.x = m_xcliplo;
  1123. WHEN_INSIDE;
  1124. }
  1125. if( aSegEnd.x > m_xcliphi )
  1126. {
  1127. temp = USCALE( (aSegEnd.y - aSegStart.y), (aSegEnd.x - m_xcliphi),
  1128. (aSegEnd.x - aSegStart.x) );
  1129. aSegEnd.y -= temp;
  1130. aSegEnd.x = m_xcliphi;
  1131. WHEN_INSIDE;
  1132. }
  1133. }
  1134. else
  1135. {
  1136. if( (aSegStart.y < m_ycliplo) || (aSegEnd.y > m_ycliphi) )
  1137. {
  1138. WHEN_OUTSIDE;
  1139. }
  1140. if( aSegStart.y > m_ycliphi )
  1141. {
  1142. temp = USCALE( (aSegEnd.x - aSegStart.x), (aSegStart.y - m_ycliphi),
  1143. (aSegStart.y - aSegEnd.y) );
  1144. if( (aSegStart.x += temp) > m_xcliphi )
  1145. {
  1146. WHEN_OUTSIDE;
  1147. }
  1148. aSegStart.y = m_ycliphi;
  1149. WHEN_INSIDE;
  1150. }
  1151. if( aSegEnd.y < m_ycliplo )
  1152. {
  1153. temp = USCALE( (aSegEnd.x - aSegStart.x), (m_ycliplo - aSegEnd.y),
  1154. (aSegStart.y - aSegEnd.y) );
  1155. if( (aSegEnd.x -= temp) < m_xcliplo )
  1156. {
  1157. WHEN_OUTSIDE;
  1158. }
  1159. aSegEnd.y = m_ycliplo;
  1160. WHEN_INSIDE;
  1161. }
  1162. if( aSegStart.x < m_xcliplo )
  1163. {
  1164. temp = USCALE( (aSegStart.y - aSegEnd.y), (m_xcliplo - aSegStart.x),
  1165. (aSegEnd.x - aSegStart.x) );
  1166. aSegStart.y -= temp;
  1167. aSegStart.x = m_xcliplo;
  1168. WHEN_INSIDE;
  1169. }
  1170. if( aSegEnd.x > m_xcliphi )
  1171. {
  1172. temp = USCALE( (aSegStart.y - aSegEnd.y), (aSegEnd.x - m_xcliphi),
  1173. (aSegEnd.x - aSegStart.x) );
  1174. aSegEnd.y += temp;
  1175. aSegEnd.x = m_xcliphi;
  1176. WHEN_INSIDE;
  1177. }
  1178. }
  1179. if( ( (aSegEnd.x + aSegStart.x) / 2 <= m_xcliphi )
  1180. && ( (aSegEnd.x + aSegStart.x) / 2 >= m_xcliplo ) \
  1181. && ( (aSegEnd.y + aSegStart.y) / 2 <= m_ycliphi )
  1182. && ( (aSegEnd.y + aSegStart.y) / 2 >= m_ycliplo ) )
  1183. {
  1184. return false;
  1185. }
  1186. else
  1187. {
  1188. return true;
  1189. }
  1190. }