MCPcopy Create free account
hub / github.com/CleanCut/rusty_engine / is_convex

Method is_convex

src/physics.rs:231–291  ·  view source on GitHub ↗

Whether the points in the collider represent a convex polygon (not concave or complex). This is important, because Rusty Engine's collision detection doesn't work correctly unless colliders are convex polygons. This implementation is based on Rory Daulton's answer on https://stackoverflow.com/questions/471962/how-do-i-efficiently-determine-if-a-polygon-is-convex-non-convex-or-complex?answertab=vo

(&self)

Source from the content-addressed store, hash-verified

229 ///
230 /// This implementation is based on Rory Daulton's answer on https://stackoverflow.com/questions/471962/how-do-i-efficiently-determine-if-a-polygon-is-convex-non-convex-or-complex?answertab=votes#tab-top
231 pub fn is_convex(&self) -> bool {
232 if let Collider::Poly(points) = self {
233 let length = points.len();
234 if length < 3 {
235 return false; // empty sets, points and lines are not convex polygons
236 }
237 // the source algorithm deals with individual x's and y's and the combined points in
238 // disjoint ways, so we need to follow the pattern unless we want to modify the
239 // algorithm itself.
240 let mut old_x = points[length - 2].x;
241 let mut old_y = points[length - 2].y;
242 let mut new_x = points[length - 1].x;
243 let mut new_y = points[length - 1].y;
244 let mut new_direction = (new_y - old_y).atan2(new_x - old_x);
245 let mut angle_sum = 0.0;
246 let mut old_direction;
247 let mut orientation = 0.0;
248 for (idx, newpoint) in points.iter().enumerate() {
249 // The fact that new_x and new_y are re-used at the top of the loop with the
250 // expectation that they have the last loop's values is why we can't use the
251 // newpoint loop variable directly. Messy. :-/
252 old_x = new_x;
253 old_y = new_y;
254 old_direction = new_direction;
255 new_x = newpoint.x;
256 new_y = newpoint.y;
257 new_direction = (new_y - old_y).atan2(new_x - old_x);
258 if (old_x == new_x) && (old_y == new_y) {
259 return false; // repeated consecutive points
260 }
261 // Calculate & check the normalized deriction-change angle
262 let mut angle = new_direction - old_direction;
263 if angle <= -PI {
264 angle += TAU; // make it in half-open interval (-Pi, Pi]
265 } else if angle > PI {
266 angle -= TAU;
267 }
268 if idx == 0 {
269 // if first time through loop, initialize orientation
270 if angle == 0.0 {
271 return false; // the source algorithm doesn't explain this one
272 }
273 if angle > 0.0 {
274 orientation = 1.0;
275 } else {
276 orientation = -1.0;
277 }
278 } else if orientation * angle <= 0.0 {
279 // not both positive or both negative
280 return false;
281 }
282
283 // Accumulate the direction-change angle
284 angle_sum += angle;
285 }
286 // Check that the total number of full turns is plus-or-minus 1
287 let full_turns = (angle_sum / TAU).abs();
288 return (full_turns > 0.9999) && (full_turns < 1.0001);

Callers 1

game_logicFunction · 0.80

Calls

no outgoing calls

Tested by

no test coverage detected