collision.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. package tamias
  2. type CollisionFunc func (a * Shape, b * Shape, c * Contact) (bool);
  3. // Add contact points for circle to circle collisions.
  4. // Used by several collision tests.
  5. func circle2circleQuery(p1, p2 Vect, r1, r2 Float, con * Contact) (int) {
  6. mindist := r1 + r2;
  7. delta := p2.Sub(p1)
  8. distsq := delta.Lengthsq()
  9. if distsq >= mindist*mindist {
  10. return 0
  11. }
  12. dist := distsq.Sqrt()
  13. // To avoid singularities, do nothing in the case of dist = 0.
  14. nz_dist := dist
  15. if dist == 0.0 {
  16. nz_dist := INFINITY
  17. }
  18. c1 := p1.Add(delta.Mult(Float(0.5) + (r1 - Float(0.5)*mindist) / nz_dist))
  19. c2 := delta.Mult(Float(1.0) / nz_dist)
  20. con.Init(c1, c2, dist - mindist, 0)
  21. return 1
  22. }
  23. // Collide circle shapes.
  24. func circle2circle(circ1, circ2 *CircleShape, arr * Contact) (int) {
  25. return circle2circleQuery(circ1.tc, circ2.tc, circ1.r, circ2.r, arr);
  26. }
  27. // Collide circles to segment shapes.
  28. func circle2segment(circ *CircleShape, seg *SegmentShape, con *Contact) (int) {
  29. // Radius sum
  30. rsum := circ.r + seg.r
  31. // Calculate normal distance from segment.
  32. dn := seg.tn.Dot(circ.tc) - seg.ta.Dot(seg.tn)
  33. dist := dn.Abs() - rsum
  34. if dist > Float(0.0) { return 0 }
  35. // Calculate tangential distance along segment.
  36. dt := - seg.tn.Cross(circ.tc)
  37. dtMin := - seg.tn.Cross(seg.ta)
  38. dtMax := - seg.tn.Cross(seg.tb)
  39. // Decision tree to decide which feature of the segment to collide with.
  40. if dt < dtMin {
  41. if dt < (dtMin - rsum) {
  42. return 0
  43. } else {
  44. return circle2circleQuery(circ.tc, seg.ta, circ.r, seg.r, con)
  45. }
  46. } else {
  47. if dt < dtMax {
  48. n := seg.tn.Neg();
  49. if dn < 0.0 {
  50. n = seg.tn
  51. }
  52. c1 := n.Mult(circ.r + dist * Float(0.5))
  53. con.Init(circ.tc, c1, n, dist, 0)
  54. return 1
  55. } else {
  56. if dt < (dtMax + rsum) {
  57. return circle2circleQuery(circ.tc, seg.tb, circ.r, seg.r, con)
  58. } else {
  59. return 0
  60. }
  61. }
  62. }
  63. return 1
  64. }
  65. // Helper function for working with contact buffers
  66. // This used to malloc/realloc memory on the fly but was repurposed.
  67. func nextContactPoint(arr []Contact, num int) (contact * cpContact, num int) {
  68. if (num <= MAX_CONTACTS_PER_ARBITER) {
  69. num = num + 1
  70. }
  71. return arr[num], num;
  72. }
  73. // Find the minimum separating axis for the given poly and axis list.
  74. func findMSA(poly *PolyShape, axes []PolyShapeAxis, num int) (result int,
  75. min_out Float) {
  76. min_index := 0
  77. min := poly.ShapeValueOnAxis(axes.n[0], axes.d[0])
  78. if min > Float(0.0) {
  79. return -1, Float(-1.0)
  80. }
  81. for i:=1; i<num; i++ {
  82. dist := poly.ShapeValueOnAxis(axes[i].n, axes[i].d)
  83. if dist > Float(0.0) {
  84. return -1, Float(-1.0)
  85. } else if dist > min {
  86. min = dist
  87. min_index = i
  88. }
  89. }
  90. min_out = min
  91. return min_index, min_out
  92. }
  93. // Add contacts for penetrating vertexes.
  94. func findVerts(arr []Contact, poly1, poly2 *PolyShape, n Vect, dist Float) {
  95. num := 0;
  96. for i:=0; i<poly1.numVerts; i++ {
  97. v := poly1.tVerts[i]
  98. if poly2.ContainsVertsPartial(v, n.Neg()) {
  99. con, num := nextContactPoint(arr, num)
  100. con.Init(v, n, dist, HASH_PAIR(poly1.Shape.hashid, i))
  101. }
  102. }
  103. for i:=0; i<poly2.numVerts; i++ {
  104. v := poly2.tVerts[i]
  105. if poly1.ContainsVertsPartial(v, n.Neg()) {
  106. con, num := nextContactPoint(arr, num)
  107. con.Init(v, n, dist, HASH_PAIR(poly2.Shape.hashid, i))
  108. }
  109. }
  110. // if(!num)
  111. // addContactPoint(arr, &size, &num, cpContactNew(shape1.body.p, n, dist, 0));
  112. return num;
  113. }
  114. // Collide poly shapes together.
  115. func poly2poly(poly1, poly2 *PolyShape, arr []cpContact) (int) {
  116. mini1, min1 := findMSA(poly2, poly1.tAxes, poly1.numVerts)
  117. if mini1 == -1 {
  118. return 0
  119. }
  120. mini2, min2 := findMSA(poly2, poly1.tAxes, poly1.numVerts)
  121. if mini2 == -1 {
  122. return 0
  123. }
  124. // There is overlap, find the penetrating verts
  125. if(min1 > min2) {
  126. return findVerts(arr, poly1, poly2, poly1.tAxes[mini1].n, min1)
  127. } else {
  128. return findVerts(arr, poly1, poly2, poly2.tAxes[mini2].n.Neg(), min2)
  129. }
  130. }
  131. // Like cpPolyValueOnAxis(), but for segments.
  132. func segValueOnAxis(seg * SegmentShape, n Vect, d Float) (Float) {
  133. a := n.dot(seg.ta) - seg.r
  134. b := n.dot(seg.tb) - seg.r
  135. return a.Min(b) - d
  136. }
  137. // Identify vertexes that have penetrated the segment.
  138. func findPointsBehindSeg(arr [] Contact, num int, seg * SegmentShape,
  139. poly PolyShape, pDist Float, coef Float) (int) {
  140. dta := seg.tn.Cross(seg.ta)
  141. dtb := seg.tn.Cross(seg.tb)
  142. n := seg.tn.Mult(coef)
  143. for i:=0; i < poly.numVerts ; i++ {
  144. v := poly.tVerts[i]
  145. if v.Dot(n) < (seg.tn.Dot(seg.ta)* coef + seg.r) {
  146. dt := seg.tn.Cross(v)
  147. if dta >= dt && dt >= dtb {
  148. con, num := nextContactPoint(arr, num)
  149. con.Init(v, n, pDist, HASH_PAIR(poly.Shape.hashid, i))
  150. }
  151. }
  152. }
  153. return num
  154. }
  155. // This one is complicated and gross. Just don't go there... (sic)
  156. // TODO: Comment me!
  157. func seg2poly(seg SegmentShape, poly *PolyShape, arr []cpContact ) (int) {
  158. axes := poly.tAxes
  159. segD := seg.tn.Dot(seg.ta)
  160. minNorm := poly.ShapeValueOnAxis(seg.tn, segD) - seg.r
  161. minNeg := poly.ShapeValueOnAxis(seg.tn.Neg(), -segD) - seg.r
  162. if minNeg > float(0.0) || minNorm > float(0.0) {
  163. return 0
  164. }
  165. // Find mimimum
  166. mini := 0
  167. poly_min := segValueOnAxis(seg, axes[0].n, axes[0].d)
  168. if poly_min > Float(0.0) {
  169. return 0
  170. }
  171. for i:=0; i < poly.numVerts ; i++ {
  172. dist := segValueOnAxis(seg, axes[i].n, axes[i].d);
  173. if dist > Float(0.0) {
  174. return 0
  175. } else if dist > poly_min {
  176. poly_min = dist
  177. mini = i
  178. }
  179. }
  180. num := 0
  181. poly_n := axes[mini].n.Neg();
  182. va := seg.ta.Add(poly_n.Mult(seg.r))
  183. vb := seg.tb.Add(poly_n.Mult(seg.r))
  184. if poly.ContainsVert(va) {
  185. con, num := nextContactPoint(arr, num)
  186. con.Init(va, poly_n, poly_min, HASH_PAIR(seg.Shape.hashid, 0))
  187. }
  188. if poly.ContainsVert(vb) {
  189. con, num := nextContactPoint(arr, num)
  190. con.Init(va, poly_n, poly_min, HASH_PAIR(seg.Shape.hashid, 1))
  191. }
  192. // Floating point precision problems here.
  193. // This will have to do for now.
  194. poly_min -= CollisionSlop
  195. if minNorm >= poly_min || minNeg >= poly_min {
  196. if(minNorm > minNeg) {
  197. num = findPointsBehindSeg(arr, num, seg, poly, minNorm, Float(1.0));
  198. } else {
  199. num = findPointsBehindSeg(arr, num, seg, poly, minNeg, Float(-1.0));
  200. }
  201. }
  202. // If no other collision points are found, try colliding endpoints.
  203. if(num == 0) {
  204. poly_a := poly.tVerts[mini]
  205. poly_b := poly.tVerts[(mini + 1)%poly.numVerts]
  206. if circle2circleQuery(seg.ta, poly_a, seg.r, 0.0f, arr[0]) > 0 {
  207. return 1;
  208. }
  209. if circle2circleQuery(seg.tb, poly_a, seg.r, 0.0f, arr[0]) > 0 {
  210. return 1;
  211. }
  212. if circle2circleQuery(seg.ta, poly_b, seg.r, 0.0f, arr[0]) > 0 {
  213. return 1;
  214. }
  215. if circle2circleQuery(seg.tb, poly_b, seg.r, 0.0f, arr[0]) > 0 {
  216. return 1;
  217. }
  218. }
  219. return num;
  220. }
  221. // This one is less gross, but still gross. (sic)
  222. // TODO: Comment me!
  223. func circle2poly(cic *CircleShape, poly *PolyShape, con *Contact) (int) {
  224. axes := poly.tAxes;
  225. // find minimum
  226. mini := 0;
  227. min := axes[0].n.Dot(circ.tc) - axes[0].d - circ.r
  228. for int i=0; i<poly.numVerts; i++ {
  229. dist := axes[i].n.Dot(circ.tc) - axes[i].d - circ.r
  230. if dist > 0.0 {
  231. return 0
  232. } else if dist > min {
  233. min = dist
  234. mini = i
  235. }
  236. }
  237. n := axes[mini].n;
  238. a := poly.tVerts[mini];
  239. b := poly.tVerts[(mini + 1)%poly.numVerts];
  240. dta := n.Cross(a)
  241. dtb := n.Cross(b)
  242. dt := n.Cross(n, circ.tc);
  243. if dt < dtb {
  244. return circle2circleQuery(circ.tc, b, circ.r, Float(0.0), con)
  245. } else if dt < dta {
  246. con.Init(circ.tc.Sub(n.Mult(circ.r + min / Float(2.0))), n.Neg(), min, 0)
  247. return 1;
  248. } else {
  249. return circle2circleQuery(circ.tc, a, circ.r, Float(0.0), con)
  250. }
  251. }
  252. /*
  253. TODO: this must probably be done differently in Go...
  254. //static const collisionFunc builtinCollisionFuncs[9] = {
  255. // circle2circle,
  256. // NULL,
  257. // NULL,
  258. // circle2segment,
  259. // NULL,
  260. // NULL,
  261. // circle2poly,
  262. // seg2poly,
  263. // poly2poly,
  264. //};
  265. //static const collisionFunc *colfuncs = builtinCollisionFuncs;
  266. static collisionFunc *colfuncs = NULL;
  267. static void
  268. addColFunc(cpShapeType a, cpShapeType b, collisionFunc func)
  269. {
  270. colfuncs[a + b*CP_NUM_SHAPES] = func;
  271. }
  272. #ifdef __cplusplus
  273. extern "C" {
  274. #endif
  275. void cpInitCollisionFuncs(void);
  276. // Initializes the array of collision functions.
  277. // Called by cpInitChipmunk().
  278. void
  279. cpInitCollisionFuncs(void)
  280. {
  281. if(!colfuncs)
  282. colfuncs = (collisionFunc *)cpcalloc(CP_NUM_SHAPES*CP_NUM_SHAPES, sizeof(collisionFunc));
  283. addColFunc(CP_CIRCLE_SHAPE, CP_CIRCLE_SHAPE, circle2circle);
  284. addColFunc(CP_CIRCLE_SHAPE, CP_SEGMENT_SHAPE, circle2segment);
  285. addColFunc(CP_SEGMENT_SHAPE, CP_POLY_SHAPE, seg2poly);
  286. addColFunc(CP_CIRCLE_SHAPE, CP_POLY_SHAPE, circle2poly);
  287. addColFunc(CP_POLY_SHAPE, CP_POLY_SHAPE, poly2poly);
  288. }
  289. #ifdef __cplusplus
  290. }
  291. #endif
  292. */
  293. func CollideShapes(cpShape *a, cpShape *b, cpContact *arr) (int) {
  294. // Their shape types must be in order.
  295. Assert(a.ShapeClass.Type <= b.ShapeClass.Type,
  296. "Collision shapes passed to cpCollideShapes() are not sorted.");
  297. /*
  298. collisionFunc cfunc = colfuncs[a.klass.type + b.klass.type*CP_NUM_SHAPES];
  299. return (cfunc) ? cfunc(a, b, arr) : 0;
  300. */
  301. // TODO: make this work
  302. return 0
  303. }