collision_shape2d.nim 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. # author: Ethosa
  2. ## It provides collision shapes.
  3. import
  4. ../core/vector2,
  5. ../core/rect2,
  6. ../core/anchor,
  7. ../core/input,
  8. ../core/enums,
  9. ../core/circle,
  10. ../core/polygon2,
  11. ../nodes/node,
  12. ../nodes/canvas,
  13. node2d
  14. when defined(debug):
  15. import
  16. math,
  17. ../thirdparty/opengl
  18. const PI2 = PI*2
  19. type
  20. CollisionShape2DType* {.size: sizeof(int8), pure.} = enum
  21. COLLISION_SHAPE_2D_RECTANGLE,
  22. COLLISION_SHAPE_2D_CIRCLE,
  23. COLLISION_SHAPE_2D_POLYGON
  24. CollisionShape2DObj* = object of Node2DObj
  25. disable*: bool
  26. x1*, y1*, radius*: float
  27. polygon*: seq[Vector2Obj]
  28. shape_type*: CollisionShape2DType
  29. CollisionShape2DRef* = ref CollisionShape2DObj
  30. proc CollisionShape2D*(name: string = "CollisionShape2D"): CollisionShape2DRef =
  31. ## Creates a new CollisionShape2D.
  32. ##
  33. ## Arguments:
  34. ## - `name` is a node name.
  35. runnableExamples:
  36. var node = CollisionShape2D("CollisionShape2D")
  37. nodepattern(CollisionShape2DRef)
  38. node2dpattern()
  39. result.rect_size.x = 40
  40. result.rect_size.y = 40
  41. result.disable = false
  42. result.x1 = 0
  43. result.y1 = 0
  44. result.radius = 20
  45. result.polygon = @[]
  46. result.centered = false
  47. result.kind = COLLISION_SHAPE_2D_NODE
  48. method setShapeTypeRect*(self: CollisionShape2DRef) {.base.} =
  49. ## Changes shape type to `circle`.
  50. self.shape_type = COLLISION_SHAPE_2D_RECTANGLE
  51. method setShapeTypeCircle*(self: CollisionShape2DRef, cx, cy, radius: float) {.base.} =
  52. ## Changes shape type to `rectangle`.
  53. ##
  54. ## Arguments:
  55. ## - `cx` is a center circle position at X axis.
  56. ## - `cy` is a center circle position at Y axis.
  57. ## - `radius` is a circle radius.
  58. self.shape_type = COLLISION_SHAPE_2D_CIRCLE
  59. self.x1 = cx
  60. self.y1 = cy
  61. self.radius = radius
  62. method setShapeTypePolygon*(self: CollisionShape2DRef, positions: varargs[Vector2Obj]) {.base.} =
  63. ## Changes shape type to `polygon`.
  64. ##
  65. ## Arguments:
  66. ## - `positions` is a varargs of polygon positions. Should be more than 2.
  67. self.shape_type = COLLISION_SHAPE_2D_POLYGON
  68. if positions.len() < 3:
  69. return
  70. self.polygon = @[]
  71. for i in positions:
  72. self.polygon.add(i)
  73. when defined(debug):
  74. method draw*(self: CollisionShape2DRef, w, h: GLfloat) =
  75. ## this method uses in the `window.nim`.
  76. {.warning[LockLevel]: off.}
  77. procCall self.Node2DRef.draw(w, h)
  78. let
  79. x = -w/2 + self.global_position.x
  80. y = h/2 - self.global_position.y
  81. if self.disable:
  82. glColor4f(0.5, 0.5, 0.5, 0.7)
  83. else:
  84. glColor4f(0.5, 0.6, 0.9, 0.7)
  85. case self.shape_type
  86. of COLLISION_SHAPE_2D_RECTANGLE:
  87. glRectf(x, y, x+self.rect_size.x, y-self.rect_size.y)
  88. of COLLISION_SHAPE_2D_CIRCLE:
  89. glBegin(GL_TRIANGLE_FAN)
  90. glVertex3f(x + self.x1, y - self.y1, self.z_index_global)
  91. for i in 0..180:
  92. let angle = PI2*i.float/180f
  93. glVertex3f(x + self.x1 + self.radius*cos(angle), y - self.y1 - self.radius*sin(angle), self.z_index_global)
  94. glEnd()
  95. of COLLISION_SHAPE_2D_POLYGON:
  96. glBegin(GL_POLYGON)
  97. for vec2 in self.polygon:
  98. glVertex3f(x + vec2.x, y - vec2.y, self.z_index_global)
  99. glEnd()
  100. method duplicate*(self: CollisionShape2DRef): CollisionShape2DRef {.base.} =
  101. ## Duplicates CollisionShape2D object and create a new CollisionShape2D.
  102. self.deepCopy()
  103. method getGlobalMousePosition*(self: CollisionShape2DRef): Vector2Obj {.inline.} =
  104. ## Returns mouse position.
  105. Vector2Obj(x: last_event.x, y: last_event.y)
  106. method isCollide*(self: CollisionShape2DRef, x, y: float): bool {.base.} =
  107. ## Checks collision with point.
  108. ##
  109. ## Arguments:
  110. ## - `x` is a point position at X axis.
  111. ## - `y` is a point position at Y axis.
  112. self.CanvasRef.calcGlobalPosition()
  113. if self.disable:
  114. return false
  115. case self.shape_type
  116. of COLLISION_SHAPE_2D_RECTANGLE:
  117. return Rect2(self.global_position, self.rect_size).contains(x, y)
  118. of COLLISION_SHAPE_2D_CIRCLE:
  119. let
  120. dx = x - self.x1
  121. dy = y - self.y1
  122. return dx*dx + dy*dy <= self.radius*self.radius
  123. of COLLISION_SHAPE_2D_POLYGON:
  124. result = false
  125. var next = 1
  126. let length = self.polygon.len()
  127. for i in 0..<length:
  128. inc next
  129. if next == length: next = 0
  130. let
  131. a = self.polygon[i] + self.global_position
  132. b = self.polygon[next] + self.global_position
  133. if ((a.y >= y and b.y < y) or (a.y < y and b.y >= y)) and (x < (b.x-a.x)*(y-a.y) / (b.y-a.y)+a.x):
  134. result = not result
  135. method isCollide*(self: CollisionShape2DRef, vec2: Vector2Obj): bool {.base.} =
  136. ## Checks collision with point.
  137. self.calcGlobalPosition()
  138. if self.disable:
  139. return false
  140. case self.shape_type
  141. of COLLISION_SHAPE_2D_RECTANGLE:
  142. return Rect2(self.global_position, self.rect_size).contains(vec2)
  143. of COLLISION_SHAPE_2D_CIRCLE:
  144. let
  145. dx = vec2.x - self.x1
  146. dy = vec2.y - self.y1
  147. return dx*dx + dy*dy <= self.radius*self.radius
  148. of COLLISION_SHAPE_2D_POLYGON:
  149. result = false
  150. var next = 1
  151. let length = self.polygon.len()
  152. for i in 0..<length:
  153. inc next
  154. if next == length: next = 0
  155. let
  156. a = self.polygon[i] + self.global_position
  157. b = self.polygon[next] + self.global_position
  158. if ((a.y >= vec2.y and b.y < vec2.y) or (a.y < vec2.y and b.y >= vec2.y)) and (vec2.x < (b.x-a.x)*(vec2.y-a.y) / (b.y-a.y)+a.x):
  159. result = not result
  160. method isCollide*(self, other: CollisionShape2DRef): bool {.base.} =
  161. ## Checks collision with other CollisionShape2D object.
  162. self.calcGlobalPosition()
  163. other.calcGlobalPosition()
  164. # Return false, if collision shape is disabled.
  165. if self.disable:
  166. return false
  167. elif other.disable:
  168. return false
  169. case self.shape_type
  170. of COLLISION_SHAPE_2D_RECTANGLE:
  171. case other.shape_type:
  172. of COLLISION_SHAPE_2D_RECTANGLE:
  173. return Rect2(other.global_position, other.rect_size).intersects(Rect2(self.global_position, self.rect_size))
  174. of COLLISION_SHAPE_2D_CIRCLE:
  175. return Rect2(self.global_position, self.rect_size).isCollideWithCircle(
  176. other.global_position.x + other.x1,
  177. other.global_position.y + other.y1, other.radius)
  178. of COLLISION_SHAPE_2D_POLYGON:
  179. var
  180. rect = Rect2(self.global_position, self.rect_size)
  181. a = Polygon2(other.polygon)
  182. a.move(other.global_position)
  183. return a.intersects(rect)
  184. of COLLISION_SHAPE_2D_CIRCLE:
  185. case other.shape_type:
  186. of COLLISION_SHAPE_2D_CIRCLE:
  187. let
  188. sradii = self.radius + other.radius
  189. dx = (other.global_position.x + other.x1) - (self.global_position.x + self.x1)
  190. dy = (other.global_position.y + other.y1) - (self.global_position.y + self.y1)
  191. return dx*dx + dy*dy <= sradii*sradii
  192. of COLLISION_SHAPE_2D_RECTANGLE:
  193. return Rect2(other.global_position, other.rect_size).isCollideWithCircle(
  194. self.global_position.x + self.x1,
  195. self.global_position.y + self.y1, self.radius)
  196. of COLLISION_SHAPE_2D_POLYGON:
  197. var
  198. circle = Circle(self.global_position.x + self.x1, self.global_position.y + self.y1, self.radius)
  199. a = Polygon2(other.polygon)
  200. a.move(other.global_position)
  201. return a.intersects(circle)
  202. of COLLISION_SHAPE_2D_POLYGON:
  203. case other.shape_type:
  204. of COLLISION_SHAPE_2D_RECTANGLE:
  205. var
  206. rect = Rect2(other.global_position, other.rect_size)
  207. a = Polygon2(self.polygon)
  208. a.move(self.global_position)
  209. return a.intersects(rect)
  210. of COLLISION_SHAPE_2D_POLYGON:
  211. var
  212. a = Polygon2(self.polygon)
  213. b = Polygon2(other.polygon)
  214. a.move(self.global_position)
  215. b.move(other.global_position)
  216. return a.intersects(b)
  217. of COLLISION_SHAPE_2D_CIRCLE:
  218. var
  219. circle = Circle(other.global_position.x + other.x1, other.global_position.y + other.y1, other.radius)
  220. a = Polygon2(self.polygon)
  221. a.move(self.global_position)
  222. return a.intersects(circle)