pointwithinshape.lua 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. #!/usr/bin/lua
  2. module('gluon.pointwithinshape', package.seeall)
  3. -- Begin https://love2d.org/wiki/PointWithinShape
  4. function PointWithinShape(shape, tx, ty)
  5. if #shape == 0 then
  6. return false
  7. elseif #shape == 1 then
  8. return shape[1].x == tx and shape[1].y == ty
  9. elseif #shape == 2 then
  10. return PointWithinLine(shape, tx, ty)
  11. else
  12. return CrossingsMultiplyTest(shape, tx, ty)
  13. end
  14. end
  15. function BoundingBox(box, tx, ty)
  16. return (box[2].x >= tx and box[2].y >= ty)
  17. and (box[1].x <= tx and box[1].y <= ty)
  18. or (box[1].x >= tx and box[2].y >= ty)
  19. and (box[2].x <= tx and box[1].y <= ty)
  20. end
  21. function colinear(line, x, y, e)
  22. e = e or 0.1
  23. m = (line[2].y - line[1].y) / (line[2].x - line[1].x)
  24. local function f(x) return line[1].y + m*(x - line[1].x) end
  25. return math.abs(y - f(x)) <= e
  26. end
  27. function PointWithinLine(line, tx, ty, e)
  28. e = e or 0.66
  29. if BoundingBox(line, tx, ty) then
  30. return colinear(line, tx, ty, e)
  31. else
  32. return false
  33. end
  34. end
  35. -------------------------------------------------------------------------
  36. -- The following function is based off code from
  37. -- [ http://erich.realtimerendering.com/ptinpoly/ ]
  38. --
  39. --[[
  40. ======= Crossings Multiply algorithm ===================================
  41. * This version is usually somewhat faster than the original published in
  42. * Graphics Gems IV; by turning the division for testing the X axis crossing
  43. * into a tricky multiplication test this part of the test became faster,
  44. * which had the additional effect of making the test for "both to left or
  45. * both to right" a bit slower for triangles than simply computing the
  46. * intersection each time. The main increase is in triangle testing speed,
  47. * which was about 15% faster; all other polygon complexities were pretty much
  48. * the same as before. On machines where division is very expensive (not the
  49. * case on the HP 9000 series on which I tested) this test should be much
  50. * faster overall than the old code. Your mileage may (in fact, will) vary,
  51. * depending on the machine and the test data, but in general I believe this
  52. * code is both shorter and faster. This test was inspired by unpublished
  53. * Graphics Gems submitted by Joseph Samosky and Mark Haigh-Hutchinson.
  54. * Related work by Samosky is in:
  55. *
  56. * Samosky, Joseph, "SectionView: A system for interactively specifying and
  57. * visualizing sections through three-dimensional medical image data",
  58. * M.S. Thesis, Department of Electrical Engineering and Computer Science,
  59. * Massachusetts Institute of Technology, 1993.
  60. *
  61. --]]
  62. --[[ Shoot a test ray along +X axis. The strategy is to compare vertex Y values
  63. * to the testing point's Y and quickly discard edges which are entirely to one
  64. * side of the test ray. Note that CONVEX and WINDING code can be added as
  65. * for the CrossingsTest() code; it is left out here for clarity.
  66. *
  67. * Input 2D polygon _pgon_ with _numverts_ number of vertices and test point
  68. * _point_, returns 1 if inside, 0 if outside.
  69. --]]
  70. function CrossingsMultiplyTest(pgon, tx, ty)
  71. local i, yflag0, yflag1, inside_flag
  72. local vtx0, vtx1
  73. local numverts = #pgon
  74. vtx0 = pgon[numverts]
  75. vtx1 = pgon[1]
  76. -- get test bit for above/below X axis
  77. yflag0 = ( vtx0.y >= ty )
  78. inside_flag = false
  79. for i=2,numverts+1 do
  80. yflag1 = ( vtx1.y >= ty )
  81. --[[ Check if endpoints straddle (are on opposite sides) of X axis
  82. * (i.e. the Y's differ); if so, +X ray could intersect this edge.
  83. * The old test also checked whether the endpoints are both to the
  84. * right or to the left of the test point. However, given the faster
  85. * intersection point computation used below, this test was found to
  86. * be a break-even proposition for most polygons and a loser for
  87. * triangles (where 50% or more of the edges which survive this test
  88. * will cross quadrants and so have to have the X intersection computed
  89. * anyway). I credit Joseph Samosky with inspiring me to try dropping
  90. * the "both left or both right" part of my code.
  91. --]]
  92. if ( yflag0 ~= yflag1 ) then
  93. --[[ Check intersection of pgon segment with +X ray.
  94. * Note if >= point's X; if so, the ray hits it.
  95. * The division operation is avoided for the ">=" test by checking
  96. * the sign of the first vertex wrto the test point; idea inspired
  97. * by Joseph Samosky's and Mark Haigh-Hutchinson's different
  98. * polygon inclusion tests.
  99. --]]
  100. if ( ((vtx1.y - ty) * (vtx0.x - vtx1.x) >= (vtx1.x - tx) * (vtx0.y - vtx1.y)) == yflag1 ) then
  101. inside_flag = not inside_flag
  102. end
  103. end
  104. -- Move to the next pair of vertices, retaining info as possible.
  105. yflag0 = yflag1
  106. vtx0 = vtx1
  107. vtx1 = pgon[i]
  108. end
  109. return inside_flag
  110. end
  111. -- End https://love2d.org/wiki/PointWithinShape