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.

171 lines
8.0 KiB

17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2007-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2007-2014 KiCad Developers, see CHANGELOG.TXT for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. /**
  25. * @file polygon_test_point_inside.cpp
  26. */
  27. #include <cmath>
  28. #include <vector>
  29. #include <PolyLine.h>
  30. /* this algo uses the the Jordan curve theorem to find if a point is inside or outside a polygon:
  31. * It run a semi-infinite line horizontally (increasing x, fixed y)
  32. * out from the test point, and count how many edges it crosses.
  33. * At each crossing, the ray switches between inside and outside.
  34. * If odd count, the test point is inside the polygon
  35. * This is called the Jordan curve theorem, or sometimes referred to as the "even-odd" test.
  36. * Take care to starting and ending points of segments outlines, when the horizontal line crosses a segment outline
  37. * exactly on an ending point:
  38. * Because the starting point of a segment is also the ending point of the previous, only one must be used.
  39. * And we do no use twice the same segment, so we do NOT use both starting and ending points of these 2 segments.
  40. * So we must use only one ending point of each segment when calculating intersections
  41. * but it cannot be always the starting or the ending point. This depend on relative position of 2 consectutive segments
  42. * Here, the ending point above the Y reference position is used
  43. * and the ending point below or equal the Y reference position is NOT used
  44. * Obviously, others cases are irrelevant because there is not intersection.
  45. */
  46. #define OUTSIDE false
  47. #define INSIDE true
  48. bool TestPointInsidePolygon( const CPOLYGONS_LIST& aPolysList,
  49. int aIdxstart,
  50. int aIdxend,
  51. int aRefx,
  52. int aRefy)
  53. /**
  54. * Function TestPointInsidePolygon
  55. * test if a point is inside or outside a polygon.
  56. * the polygon must have only lines (not arcs) for outlines.
  57. * @param aPolysList: the list of polygons
  58. * @param aIdxstart: the starting point of a given polygon in m_FilledPolysList.
  59. * @param aIdxend: the ending point of this polygon in m_FilledPolysList.
  60. * @param aRefx, aRefy: the point coordinate to test
  61. * @return true if the point is inside, false for outside
  62. */
  63. {
  64. // count intersection points to right of (refx,refy). If odd number, point (refx,refy) is inside polyline
  65. int ics, ice;
  66. int count = 0;
  67. // find all intersection points of line with polyline sides
  68. for( ics = aIdxstart, ice = aIdxend; ics <= aIdxend; ice = ics++ )
  69. {
  70. int seg_startX = aPolysList.GetX( ics );
  71. int seg_startY = aPolysList.GetY( ics );
  72. int seg_endX = aPolysList.GetX( ice );
  73. int seg_endY = aPolysList.GetY( ice );
  74. /* Trivial cases: skip if ref above or below the segment to test */
  75. if( ( seg_startY > aRefy ) && (seg_endY > aRefy ) )
  76. continue;
  77. // segment below ref point, or one of its ends has the same Y pos as the ref point: skip
  78. // So we eliminate one end point of 2 consecutive segments.
  79. // Note: also we skip horizontal segments if ref point is on this horizontal line
  80. // So reference points on horizontal segments outlines always are seen as outside the polygon
  81. if( ( seg_startY <= aRefy ) && (seg_endY <= aRefy ) )
  82. continue;
  83. /* refy is between seg_startY and seg_endY.
  84. * note: here: horizontal segments (seg_startY == seg_endY) are skipped,
  85. * either by the first test or by the second test
  86. * see if an horizontal semi infinite line from refx is intersecting the segment
  87. */
  88. // calculate the x position of the intersection of this segment and the semi infinite line
  89. // this is more easier if we move the X,Y axis origin to the segment start point:
  90. seg_endX -= seg_startX;
  91. seg_endY -= seg_startY;
  92. double newrefx = (double) (aRefx - seg_startX);
  93. double newrefy = (double) (aRefy - seg_startY);
  94. // Now calculate the x intersection coordinate of the line from (0,0) to (seg_endX,seg_endY)
  95. // with the horizontal line at the new refy position
  96. // the line slope = seg_endY/seg_endX;
  97. // and the x pos relative to the new origin is intersec_x = refy/slope
  98. // Note: because horizontal segments are skipped, 1/slope exists (seg_endY never == O)
  99. double intersec_x = (newrefy * seg_endX) / seg_endY;
  100. if( newrefx < intersec_x ) // Intersection found with the semi-infinite line from refx to infinite
  101. count++;
  102. }
  103. return count & 1 ? INSIDE : OUTSIDE;
  104. }
  105. /* Function TestPointInsidePolygon (overlaid)
  106. * same as previous, but use wxPoint and aCount corners
  107. */
  108. bool TestPointInsidePolygon( const wxPoint *aPolysList, int aCount, const wxPoint &aRefPoint )
  109. {
  110. // count intersection points to right of (refx,refy). If odd number, point (refx,refy) is inside polyline
  111. int ics, ice;
  112. int count = 0;
  113. // find all intersection points of line with polyline sides
  114. for( ics = 0, ice = aCount-1; ics < aCount; ice = ics++ )
  115. {
  116. int seg_startX = aPolysList[ics].x;
  117. int seg_startY = aPolysList[ics].y;
  118. int seg_endX = aPolysList[ice].x;
  119. int seg_endY = aPolysList[ice].y;
  120. /* Trivial cases: skip if ref above or below the segment to test */
  121. if( ( seg_startY > aRefPoint.y ) && (seg_endY > aRefPoint.y ) )
  122. continue;
  123. // segment below ref point, or one of its ends has the same Y pos as the ref point: skip
  124. // So we eliminate one end point of 2 consecutive segments.
  125. // Note: also we skip horizontal segments if ref point is on this horizontal line
  126. // So reference points on horizontal segments outlines always are seen as outside the polygon
  127. if( ( seg_startY <= aRefPoint.y ) && (seg_endY <= aRefPoint.y ) )
  128. continue;
  129. /* refy is between seg_startY and seg_endY.
  130. * note: here: horizontal segments (seg_startY == seg_endY) are skipped,
  131. * either by the first test or by the second test
  132. * see if an horizontal semi infinite line from refx is intersecting the segment
  133. */
  134. // calculate the x position of the intersection of this segment and the semi infinite line
  135. // this is more easier if we move the X,Y axis origin to the segment start point:
  136. seg_endX -= seg_startX;
  137. seg_endY -= seg_startY;
  138. double newrefx = (double) (aRefPoint.x - seg_startX);
  139. double newrefy = (double) (aRefPoint.y - seg_startY);
  140. // Now calculate the x intersection coordinate of the line from (0,0) to (seg_endX,seg_endY)
  141. // with the horizontal line at the new refy position
  142. // the line slope = seg_endY/seg_endX;
  143. // and the x pos relative to the new origin is intersec_x = refy/slope
  144. // Note: because horizontal segments are skipped, 1/slope exists (seg_endY never == O)
  145. double intersec_x = (newrefy * seg_endX) / seg_endY;
  146. if( newrefx < intersec_x ) // Intersection found with the semi-infinite line from refx to infinite
  147. count++;
  148. }
  149. return count & 1 ? INSIDE : OUTSIDE;
  150. }