geom.lua 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. geom = {}
  2. function geom.on_shape(x,y, drawing, shape)
  3. if shape.mode == 'freehand' then
  4. return geom.on_freehand(x,y, drawing, shape)
  5. elseif shape.mode == 'line' then
  6. return geom.on_line(x,y, drawing, shape)
  7. elseif shape.mode == 'manhattan' then
  8. local p1 = drawing.points[shape.p1]
  9. local p2 = drawing.points[shape.p2]
  10. if p1.x == p2.x then
  11. if x ~= p1.x then return false end
  12. local y1,y2 = p1.y, p2.y
  13. if y1 > y2 then
  14. y1,y2 = y2,y1
  15. end
  16. return y >= y1-2 and y <= y2+2
  17. elseif p1.y == p2.y then
  18. if y ~= p1.y then return false end
  19. local x1,x2 = p1.x, p2.x
  20. if x1 > x2 then
  21. x1,x2 = x2,x1
  22. end
  23. return x >= x1-2 and x <= x2+2
  24. end
  25. elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
  26. return geom.on_polygon(x,y, drawing, shape)
  27. elseif shape.mode == 'circle' then
  28. local center = drawing.points[shape.center]
  29. local dist = geom.dist(center.x,center.y, x,y)
  30. return dist > shape.radius*0.95 and dist < shape.radius*1.05
  31. elseif shape.mode == 'arc' then
  32. local center = drawing.points[shape.center]
  33. local dist = geom.dist(center.x,center.y, x,y)
  34. if dist < shape.radius*0.95 or dist > shape.radius*1.05 then
  35. return false
  36. end
  37. return geom.angle_between(center.x,center.y, x,y, shape.start_angle,shape.end_angle)
  38. elseif shape.mode == 'deleted' then
  39. else
  40. assert(false, ('unknown drawing mode %s'):format(shape.mode))
  41. end
  42. end
  43. function geom.on_freehand(x,y, drawing, shape)
  44. local prev
  45. for _,p in ipairs(shape.points) do
  46. if prev then
  47. if geom.on_line(x,y, drawing, {p1=prev, p2=p}) then
  48. return true
  49. end
  50. end
  51. prev = p
  52. end
  53. return false
  54. end
  55. function geom.on_line(x,y, drawing, shape)
  56. local p1,p2
  57. if type(shape.p1) == 'number' then
  58. p1 = drawing.points[shape.p1]
  59. p2 = drawing.points[shape.p2]
  60. else
  61. p1 = shape.p1
  62. p2 = shape.p2
  63. end
  64. if p1.x == p2.x then
  65. if math.abs(p1.x-x) > 2 then
  66. return false
  67. end
  68. local y1,y2 = p1.y,p2.y
  69. if y1 > y2 then
  70. y1,y2 = y2,y1
  71. end
  72. return y >= y1-2 and y <= y2+2
  73. end
  74. -- has the right slope and intercept
  75. local m = (p2.y - p1.y) / (p2.x - p1.x)
  76. local yp = p1.y + m*(x-p1.x)
  77. if yp < y-2 or yp > y+2 then
  78. return false
  79. end
  80. -- between endpoints
  81. local k = (x-p1.x) / (p2.x-p1.x)
  82. return k > -0.005 and k < 1.005
  83. end
  84. function geom.on_polygon(x,y, drawing, shape)
  85. local prev
  86. for _,p in ipairs(shape.vertices) do
  87. if prev then
  88. if geom.on_line(x,y, drawing, {p1=prev, p2=p}) then
  89. return true
  90. end
  91. end
  92. prev = p
  93. end
  94. return geom.on_line(x,y, drawing, {p1=shape.vertices[1], p2=shape.vertices[#shape.vertices]})
  95. end
  96. -- are (x3,y3) and (x4,y4) on the same side of the line between (x1,y1) and (x2,y2)
  97. function geom.same_side(x1,y1, x2,y2, x3,y3, x4,y4)
  98. if x1 == x2 then
  99. return math.sign(x3-x1) == math.sign(x4-x1)
  100. end
  101. if y1 == y2 then
  102. return math.sign(y3-y1) == math.sign(y4-y1)
  103. end
  104. local m = (y2-y1)/(x2-x1)
  105. return math.sign(m*(x3-x1) + y1-y3) == math.sign(m*(x4-x1) + y1-y4)
  106. end
  107. function math.sign(x)
  108. if x > 0 then
  109. return 1
  110. elseif x == 0 then
  111. return 0
  112. elseif x < 0 then
  113. return -1
  114. end
  115. end
  116. function geom.angle_with_hint(x1, y1, x2, y2, hint)
  117. local result = geom.angle(x1,y1, x2,y2)
  118. if hint then
  119. -- Smooth the discontinuity where angle goes from positive to negative.
  120. -- The hint is a memory of which way we drew it last time.
  121. while result > hint+math.pi/10 do
  122. result = result-math.pi*2
  123. end
  124. while result < hint-math.pi/10 do
  125. result = result+math.pi*2
  126. end
  127. end
  128. return result
  129. end
  130. -- result is from -π/2 to 3π/2, approximately adding math.atan2 from Lua 5.3
  131. -- (LÖVE is Lua 5.1)
  132. function geom.angle(x1,y1, x2,y2)
  133. local result = math.atan((y2-y1)/(x2-x1))
  134. if x2 < x1 then
  135. result = result+math.pi
  136. end
  137. return result
  138. end
  139. -- is the line between x,y and cx,cy at an angle between s and e?
  140. function geom.angle_between(ox,oy, x,y, s,e)
  141. local angle = geom.angle(ox,oy, x,y)
  142. if s > e then
  143. s,e = e,s
  144. end
  145. -- I'm not sure this is right or ideal..
  146. angle = angle-math.pi*2
  147. if s <= angle and angle <= e then
  148. return true
  149. end
  150. angle = angle+math.pi*2
  151. if s <= angle and angle <= e then
  152. return true
  153. end
  154. angle = angle+math.pi*2
  155. return s <= angle and angle <= e
  156. end
  157. function geom.dist(x1,y1, x2,y2) return ((x2-x1)^2+(y2-y1)^2)^0.5 end