1use sdl2::rect::Rect;
4
5#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
7pub enum CollisionShape {
8 Circle {
9 center: (i32, i32),
10 radius: u32
11 }, Rect {
12 center: (i32, i32),
13 size: (u32, u32)
14 }, Polygon {
15 center: (i32, i32),
16 points: Vec<(i32, i32)>
17 }
18}
19
20impl CollisionShape {
21 pub fn collides_with(&self, other: &CollisionShape) -> bool {
22 match self {
23 CollisionShape::Circle { center, radius } => {
24 match other {
25 CollisionShape::Circle { center: other_center, radius: other_radius } => {
26 let a = other_center.0 - center.0;
27 let b = other_center.1 - center.1;
28 let c = other_radius + radius;
29 ((a * a) + (b * b)) as u32 <= (c * c)
30 }, CollisionShape::Rect { center: other_center, size: other_size } => {
31 let mut test = center.clone();
32 let rect = Rect::new(
33 other_center.0 - other_size.0 as i32 / 2,
34 other_center.1 - other_size.1 as i32 / 2,
35 other_size.0,
36 other_size.1
37 );
38 if center.0 < rect.x {
39 test.0 = rect.x;
40 } else if center.0 > rect.x + rect.w {
41 test.0 = rect.x + rect.w;
42 }
43 if center.1 < rect.y {
44 test.1 = rect.y;
45 } else if center.1 > rect.y + rect.h {
46 test.1 = rect.y + rect.h;
47 }
48 let dist_lat = (center.0 - test.0, center.1 - test.1);
49 let dist_sqrd = (dist_lat.0 * dist_lat.0) + (dist_lat.1 * dist_lat.1);
50 dist_sqrd as u32 <= radius * radius
51 }, CollisionShape::Polygon { center: other_center, points } => {
52 for point in points.iter() {
53 let dists = (
54 other_center.0 + point.0 - center.0,
55 other_center.1 + point.1 - center.1
56 );
57 let dist_sqrd = dists.0 * dists.0 + dists.1 + dists.1;
58 if (dist_sqrd as u32) < radius * radius {
59 return true;
60 }
61 }
62 false
63 }
64 }
65 }, CollisionShape::Rect { center, size } => {
66 match other {
67 CollisionShape::Circle { center: other_center, radius: other_radius } => {
68 let mut test = other_center.clone();
69 let rect = Rect::new(
70 center.0 - size.0 as i32 / 2,
71 center.1 - size.1 as i32 / 2,
72 size.0,
73 size.1
74 );
75 if other_center.0 < rect.x {
76 test.0 = rect.x;
77 } else if other_center.0 > rect.x + rect.w {
78 test.0 = rect.x + rect.w;
79 }
80 if other_center.1 < rect.y {
81 test.1 = rect.y;
82 } else if other_center.1 > rect.y + rect.h {
83 test.1 = rect.y + rect.h;
84 }
85 let dist_lat = (other_center.0 - test.0, other_center.1 - test.1);
86 let dist_sqrd = (dist_lat.0 * dist_lat.0) + (dist_lat.1 * dist_lat.1);
87 dist_sqrd as u32 <= other_radius * other_radius
88 }, CollisionShape::Rect { center: other_center, size: other_size } => {
89 let r1 = Rect::new(
90 center.0 - size.0 as i32 / 2,
91 center.1 - size.1 as i32 / 2,
92 size.0,
93 size.1
94 );
95 let r2 = Rect::new(
96 other_center.0 - other_size.1 as i32 / 2,
97 other_center.1 - other_size.1 as i32 / 2,
98 other_size.0,
99 other_size.1
100 );
101 r1.x + r1.w >= r2.x
102 && r1.x <= r2.x + r2.w
103 && r1.y + r1.h >= r2.y
104 && r1.y <= r2.y + r2.h
105 }, CollisionShape::Polygon { center: other_center, points } => {
106 for point in points.iter() {
107 let transformed_point = (
108 other_center.0 + point.0,
109 other_center.1 + point.1
110 );
111 let (left, right) = (
112 center.0 - size.0 as i32 / 2,
113 center.0 + size.0 as i32 / 2
114 );
115 let (top, bottom) = (
116 center.1 - size.1 as i32 / 2,
117 center.1 + size.1 as i32 / 2
118 );
119 if transformed_point.0 >= left
120 && transformed_point.0 <= right
121 && transformed_point.1 >= top
122 && transformed_point.1 <= bottom {
123 return true;
124 }
125 }
126 false
127 }
128 }
129 }, CollisionShape::Polygon { center, points } => {
130 match other {
131 CollisionShape::Circle { center: other_center, radius } => {
132 for point in points.iter() {
133 let dists = (
134 center.0 + point.0 - other_center.0,
135 center.1 + point.1 - other_center.1
136 );
137 let dist_sqrd = dists.0 * dists.0 + dists.1 + dists.1;
138 if (dist_sqrd as u32) < radius * radius {
139 return true;
140 }
141 }
142 false
143 }, CollisionShape::Rect { center: other_center, size } => {
144 for point in points.iter() {
145 let transformed_point = (
146 center.0 + point.0,
147 center.1 + point.1
148 );
149 let (left, right) = (
150 other_center.0 - size.0 as i32 / 2,
151 other_center.0 + size.0 as i32 / 2
152 );
153 let (top, bottom) = (
154 other_center.1 - size.1 as i32 / 2,
155 other_center.1 + size.1 as i32 / 2
156 );
157 if transformed_point.0 >= left
158 && transformed_point.0 <= right
159 && transformed_point.1 >= top
160 && transformed_point.1 <= bottom {
161 return true;
162 }
163 }
164 false
165 }, CollisionShape::Polygon { center: other_center, points: other_points } => {
166 for i in 0..points.len() {
168 let p1 = points[i];
169 let p2 = points[(i + 1) % points.len()];
170
171 let normal = ((p2.1 - p1.1), -(p2.0 - p1.0));
173
174 let (min1, max1) = project_polygon(&points, &normal, ¢er);
176 let (min2, max2) = project_polygon(
177 &other_points, &normal, &other_center
178 );
179
180 if max1 < min2 || max2 < min1 {
182 return false;
183 }
184 }
185 true
186 }
187 }
188 }
189 }
190 }
191}
192
193fn project_polygon(points: &[(i32, i32)], axis: &(i32, i32), center: &(i32, i32)) -> (i32, i32) {
194 let mut min = i32::MAX;
195 let mut max = i32::MIN;
196 for point in points.iter() {
197 let proj_point = (point.0 + center.0, point.1 + center.1);
198 let dot_prod = proj_point.0 * axis.0 + proj_point.1 * axis.1;
199 if dot_prod < min {
200 min = dot_prod;
201 }
202 if dot_prod > max {
203 max = dot_prod;
204 }
205 }
206 (min, max)
207}
208