1 module prova.collision.resolve;
2 
3 import prova.collision,
4        prova.math;
5 
6 // Helper functions for getting a displacement vector that allows colliderA to escape colliderB
7 
8 // pushes to the left by the smallest value representable by a floating point
9 Vector2 resolvePointPoint(PointCollider colliderA, PointCollider colliderB)
10 {
11   return Vector2(-float.min_normal, 0);
12 }
13 
14 Vector2 resolvePointCircle(PointCollider colliderA, CircleCollider colliderB)
15 {
16   const Vector2 difference = colliderA.getPosition() - colliderB.getPosition();
17   Vector2 resolution = difference;
18 
19   resolution.setMagnitude(colliderB.radius);
20   resolution -= difference;
21 
22   return resolution;
23 }
24 
25 Vector2 resolvePointRect(PointCollider colliderA, RectCollider colliderB)
26 {
27   const Vector2 position = colliderA.getPosition();
28   const Rect bounds = colliderB.getBounds();
29   const Side side = bounds.getClosestSide(position);
30   Vector2 resolution;
31 
32   if(side == Side.LEFT)
33     resolution.x = bounds.left - position.x;
34   else if(side == Side.RIGHT)
35     resolution.x = bounds.right - position.x;
36   else if(side == Side.TOP)
37     resolution.y = bounds.top - position.y;
38   else if(side == Side.BOTTOM)
39     resolution.y = bounds.bottom - position.y;
40 
41   return resolution;
42 }
43 
44 Vector2 resolveCircleCircle(CircleCollider colliderA, CircleCollider colliderB)
45 {
46   const Vector2 difference = colliderA.getPosition() - colliderB.getPosition();
47   Vector2 resolution = difference;
48 
49   resolution.setMagnitude(colliderA.radius + colliderB.radius);
50   resolution -= difference;
51 
52   return resolution;
53 }
54 
55 Vector2 resolveCircleRect(CircleCollider colliderA, RectCollider colliderB)
56 {
57   Vector2 circlePosition = colliderA.getPosition();
58   Rect circleBounds = colliderA.getBounds();
59   Rect rectBounds = colliderB.getBounds();
60   bool topBottom = circlePosition.x < rectBounds.right && circlePosition.x > rectBounds.left;
61   bool leftRight = circlePosition.y < rectBounds.top && circlePosition.y > rectBounds.bottom;
62 
63   // touching a side
64   if(leftRight || topBottom)
65     return resolveRectBounds(circleBounds, rectBounds);
66 
67   // on a corner
68   return resolveCircleCorners(colliderA, rectBounds);
69 }
70 
71 Vector2 resolveCircleCorners(CircleCollider circle, Rect rectBounds)
72 {
73   Vector2 circlePosition = circle.getPosition();
74   Vector2 rectPosition = rectBounds.getCenter();
75   Vector2[4] corners = [
76     rectBounds.getTopLeft(),
77     rectBounds.getTopRight(),
78     rectBounds.getBottomLeft(),
79     rectBounds.getBottomRight()
80   ];
81 
82   Vector2 resolution;
83 
84   foreach(Vector2 corner; corners)
85   {
86     if(corner.distanceTo(circlePosition) > circle.radius)
87       continue;
88 
89     const Vector2 difference = circlePosition - corner;
90     resolution = difference;
91 
92     resolution.setMagnitude(circle.radius + 1e-06);
93     resolution -= difference;
94 
95     break;
96   }
97 
98   return resolution;
99 }
100 
101 Vector2 resolveRectRect(RectCollider colliderA, RectCollider colliderB)
102 {
103   const Rect boundsA = colliderA.getBounds();
104   const Rect boundsB = colliderB.getBounds();
105 
106   return resolveRectBounds(boundsA, boundsB);
107 }
108 
109 Vector2 resolveRectBounds(Rect boundsA, Rect boundsB)
110 {
111   const Side side = boundsB.getClosestSide(boundsA);
112   Vector2 resolution;
113 
114   if(side == Side.LEFT)
115     resolution.x = boundsB.left - boundsA.right;
116   else if(side == Side.RIGHT)
117     resolution.x = boundsB.right - boundsA.left;
118   else if(side == Side.TOP)
119     resolution.y = boundsB.top - boundsA.bottom;
120   else if(side == Side.BOTTOM)
121     resolution.y = boundsB.bottom - boundsA.top;
122 
123   return resolution;
124 }