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.

147 lines
6.9 KiB

17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
  1. /**
  2. * @file polygon_test_point_inside.cpp
  3. */
  4. #include <cmath>
  5. #include <vector>
  6. #include <PolyLine.h>
  7. /* this algo uses the the Jordan curve theorem to find if a point is inside or outside a polygon:
  8. * It run a semi-infinite line horizontally (increasing x, fixed y)
  9. * out from the test point, and count how many edges it crosses.
  10. * At each crossing, the ray switches between inside and outside.
  11. * If odd count, the test point is inside the polygon
  12. * This is called the Jordan curve theorem, or sometimes referred to as the "even-odd" test.
  13. * Take care to starting and ending points of segments outlines, when the horizontal line crosses a segment outline
  14. * exactly on an ending point:
  15. * Because the starting point of a segment is also the ending point of the previous, only one must be used.
  16. * And we do no use twice the same segment, so we do NOT use both starting and ending points of these 2 segments.
  17. * So we must use only one ending point of each segment when calculating intersections
  18. * but it cannot be always the starting or the ending point. This depend on relative position of 2 consectutive segments
  19. * Here, the ending point above the Y reference position is used
  20. * and the ending point below or equal the Y reference position is NOT used
  21. * Obviously, others cases are irrelevant because there is not intersection.
  22. */
  23. #define OUTSIDE false
  24. #define INSIDE true
  25. bool TestPointInsidePolygon( std::vector <CPolyPt> aPolysList,
  26. int aIdxstart,
  27. int aIdxend,
  28. int aRefx,
  29. int aRefy)
  30. /**
  31. * Function TestPointInsidePolygon
  32. * test if a point is inside or outside a polygon.
  33. * the polygon must have only lines (not arcs) for outlines.
  34. * @param aPolysList: the list of polygons
  35. * @param aIdxstart: the starting point of a given polygon in m_FilledPolysList.
  36. * @param aIdxend: the ending point of this polygon in m_FilledPolysList.
  37. * @param aRefx, aRefy: the point coordinate to test
  38. * @return true if the point is inside, false for outside
  39. */
  40. {
  41. // count intersection points to right of (refx,refy). If odd number, point (refx,refy) is inside polyline
  42. int ics, ice;
  43. int count = 0;
  44. // find all intersection points of line with polyline sides
  45. for( ics = aIdxstart, ice = aIdxend; ics <= aIdxend; ice = ics++ )
  46. {
  47. int seg_startX = aPolysList[ics].x;
  48. int seg_startY = aPolysList[ics].y;
  49. int seg_endX = aPolysList[ice].x;
  50. int seg_endY = aPolysList[ice].y;
  51. /* Trivial cases: skip if ref above or below the segment to test */
  52. if( ( seg_startY > aRefy ) && (seg_endY > aRefy ) )
  53. continue;
  54. // segment below ref point, or one of its ends has the same Y pos as the ref point: skip
  55. // So we eliminate one end point of 2 consecutive segments.
  56. // Note: also we skip horizontal segments if ref point is on this horizontal line
  57. // So reference points on horizontal segments outlines always are seen as outside the polygon
  58. if( ( seg_startY <= aRefy ) && (seg_endY <= aRefy ) )
  59. continue;
  60. /* refy is between seg_startY and seg_endY.
  61. * note: here: horizontal segments (seg_startY == seg_endY) are skipped,
  62. * either by the first test or by the second test
  63. * see if an horizontal semi infinite line from refx is intersecting the segment
  64. */
  65. // calculate the x position of the intersection of this segment and the semi infinite line
  66. // this is more easier if we move the X,Y axis origin to the segment start point:
  67. seg_endX -= seg_startX;
  68. seg_endY -= seg_startY;
  69. double newrefx = (double) (aRefx - seg_startX);
  70. double newrefy = (double) (aRefy - seg_startY);
  71. // Now calculate the x intersection coordinate of the line from (0,0) to (seg_endX,seg_endY)
  72. // with the horizontal line at the new refy position
  73. // the line slope = seg_endY/seg_endX;
  74. // and the x pos relative to the new origin is intersec_x = refy/slope
  75. // Note: because horizontal segments are skipped, 1/slope exists (seg_endY never == O)
  76. double intersec_x = (newrefy * seg_endX) / seg_endY;
  77. if( newrefx < intersec_x ) // Intersection found with the semi-infinite line from refx to infinite
  78. count++;
  79. }
  80. return count & 1 ? INSIDE : OUTSIDE;
  81. }
  82. /* Function TestPointInsidePolygon (overlaid)
  83. * same as previous, but use wxPoint and aCount corners
  84. */
  85. bool TestPointInsidePolygon( wxPoint *aPolysList, int aCount,wxPoint aRefPoint )
  86. {
  87. // count intersection points to right of (refx,refy). If odd number, point (refx,refy) is inside polyline
  88. int ics, ice;
  89. int count = 0;
  90. // find all intersection points of line with polyline sides
  91. for( ics = 0, ice = aCount-1; ics < aCount; ice = ics++ )
  92. {
  93. int seg_startX = aPolysList[ics].x;
  94. int seg_startY = aPolysList[ics].y;
  95. int seg_endX = aPolysList[ice].x;
  96. int seg_endY = aPolysList[ice].y;
  97. /* Trivial cases: skip if ref above or below the segment to test */
  98. if( ( seg_startY > aRefPoint.y ) && (seg_endY > aRefPoint.y ) )
  99. continue;
  100. // segment below ref point, or one of its ends has the same Y pos as the ref point: skip
  101. // So we eliminate one end point of 2 consecutive segments.
  102. // Note: also we skip horizontal segments if ref point is on this horizontal line
  103. // So reference points on horizontal segments outlines always are seen as outside the polygon
  104. if( ( seg_startY <= aRefPoint.y ) && (seg_endY <= aRefPoint.y ) )
  105. continue;
  106. /* refy is between seg_startY and seg_endY.
  107. * note: here: horizontal segments (seg_startY == seg_endY) are skipped,
  108. * either by the first test or by the second test
  109. * see if an horizontal semi infinite line from refx is intersecting the segment
  110. */
  111. // calculate the x position of the intersection of this segment and the semi infinite line
  112. // this is more easier if we move the X,Y axis origin to the segment start point:
  113. seg_endX -= seg_startX;
  114. seg_endY -= seg_startY;
  115. double newrefx = (double) (aRefPoint.x - seg_startX);
  116. double newrefy = (double) (aRefPoint.y - seg_startY);
  117. // Now calculate the x intersection coordinate of the line from (0,0) to (seg_endX,seg_endY)
  118. // with the horizontal line at the new refy position
  119. // the line slope = seg_endY/seg_endX;
  120. // and the x pos relative to the new origin is intersec_x = refy/slope
  121. // Note: because horizontal segments are skipped, 1/slope exists (seg_endY never == O)
  122. double intersec_x = (newrefy * seg_endX) / seg_endY;
  123. if( newrefx < intersec_x ) // Intersection found with the semi-infinite line from refx to infinite
  124. count++;
  125. }
  126. return count & 1 ? INSIDE : OUTSIDE;
  127. }