1use std::collections::{HashMap, HashSet};
2
3use super::broadphase::SpatialHash;
4use super::constraints::{solve_constraints, solve_constraints_position};
5use super::integrate::integrate;
6use super::broadphase::SPECULATIVE_MARGIN;
7use super::narrowphase::{test_collision, test_collision_manifold_speculative};
8use super::resolve::{
9 initialize_contacts, initialize_manifolds, resolve_contacts_position,
10 resolve_contacts_velocity_iteration, resolve_manifolds_position,
11 resolve_manifolds_velocity_iteration, warm_start_contacts, warm_start_manifolds,
12};
13use super::sleep::update_sleep;
14use super::types::*;
15
16pub struct PhysicsWorld {
17 bodies: Vec<Option<RigidBody>>,
18 free_ids: Vec<BodyId>,
19 next_id: BodyId,
20 constraints: Vec<Constraint>,
21 next_constraint_id: ConstraintId,
22 gravity: (f32, f32),
23 fixed_dt: f32,
24 accumulator: f32,
25 contacts: Vec<Contact>,
26 manifolds: Vec<ContactManifold>,
28 frame_contacts: Vec<Contact>,
33 frame_contact_pairs: HashSet<(BodyId, BodyId)>,
35 broadphase: SpatialHash,
36 solver_iterations: usize,
37 warm_cache: HashMap<(BodyId, BodyId), (f32, f32)>,
40 manifold_warm_cache: HashMap<(BodyId, BodyId, ContactID), (f32, f32)>,
42 use_manifolds: bool,
44}
45
46impl PhysicsWorld {
47 pub fn new(gravity_x: f32, gravity_y: f32) -> Self {
48 Self {
49 bodies: Vec::new(),
50 free_ids: Vec::new(),
51 next_id: 0,
52 constraints: Vec::new(),
53 next_constraint_id: 0,
54 gravity: (gravity_x, gravity_y),
55 fixed_dt: 1.0 / 60.0,
56 accumulator: 0.0,
57 contacts: Vec::new(),
58 manifolds: Vec::new(),
59 frame_contacts: Vec::new(),
60 frame_contact_pairs: HashSet::new(),
61 broadphase: SpatialHash::new(64.0),
62 solver_iterations: 10,
65 warm_cache: HashMap::new(),
66 manifold_warm_cache: HashMap::new(),
67 use_manifolds: true,
69 }
70 }
71
72 pub fn set_use_manifolds(&mut self, use_manifolds: bool) {
75 self.use_manifolds = use_manifolds;
76 }
77
78 pub fn step(&mut self, dt: f32) {
88 self.accumulator += dt;
89
90 self.frame_contacts.clear();
92 self.frame_contact_pairs.clear();
93
94 while self.accumulator >= self.fixed_dt {
95 if self.use_manifolds {
96 self.step_manifolds(self.fixed_dt);
98 } else {
99 let sub_dt = self.fixed_dt / 4.0;
101 for _ in 0..4 {
102 self.sub_step_legacy(sub_dt);
103
104 for contact in &self.contacts {
106 let key = (contact.body_a.min(contact.body_b), contact.body_a.max(contact.body_b));
107 if self.frame_contact_pairs.insert(key) {
108 self.frame_contacts.push(contact.clone());
109 }
110 }
111 }
112 }
113 self.accumulator -= self.fixed_dt;
114 }
115 }
116
117 fn step_manifolds(&mut self, fixed_dt: f32) {
120 let sub_dt = fixed_dt / 4.0;
121
122 for sub_step in 0..4 {
123 for body in self.bodies.iter_mut().flatten() {
125 integrate(body, self.gravity.0, self.gravity.1, sub_dt);
126 }
127
128 self.broadphase.clear();
130 for body in self.bodies.iter().flatten() {
131 let (min_x, min_y, max_x, max_y) = get_shape_aabb(body);
132 self.broadphase.insert_speculative(
133 body.id, min_x, min_y, max_x, max_y,
134 body.vx, body.vy, sub_dt,
135 );
136 }
137 let pairs = self.broadphase.get_pairs();
138
139 self.manifolds.clear();
141 self.contacts.clear();
142 for (id_a, id_b) in &pairs {
143 let a_idx = *id_a as usize;
144 let b_idx = *id_b as usize;
145
146 let (layer_a, mask_a, sleeping_a) = match &self.bodies[a_idx] {
147 Some(b) => (b.layer, b.mask, b.sleeping),
148 None => continue,
149 };
150 let (layer_b, mask_b, sleeping_b) = match &self.bodies[b_idx] {
151 Some(b) => (b.layer, b.mask, b.sleeping),
152 None => continue,
153 };
154
155 if (layer_a & mask_b) == 0 || (layer_b & mask_a) == 0 {
156 continue;
157 }
158
159 if sleeping_a && sleeping_b {
160 continue;
161 }
162
163 let body_a = self.bodies[a_idx].as_ref().unwrap();
164 let body_b = self.bodies[b_idx].as_ref().unwrap();
165
166 let speculative_margin = SPECULATIVE_MARGIN + (body_a.vx.abs() + body_a.vy.abs() + body_b.vx.abs() + body_b.vy.abs()) * sub_dt;
167 if let Some(manifold) = test_collision_manifold_speculative(body_a, body_b, speculative_margin) {
168 if !manifold.points.is_empty() {
169 let point = &manifold.points[0];
170 let cos_a = body_a.angle.cos();
171 let sin_a = body_a.angle.sin();
172 let cpx = point.local_a.0 * cos_a - point.local_a.1 * sin_a + body_a.x;
173 let cpy = point.local_a.0 * sin_a + point.local_a.1 * cos_a + body_a.y;
174 self.contacts.push(Contact {
175 body_a: manifold.body_a,
176 body_b: manifold.body_b,
177 normal: manifold.normal,
178 penetration: point.penetration,
179 contact_point: (cpx, cpy),
180 accumulated_jn: 0.0,
181 accumulated_jt: 0.0,
182 velocity_bias: 0.0,
183 tangent: manifold.tangent,
184 });
185 }
186 self.manifolds.push(manifold);
187 }
188 }
189
190 if sub_step == 0 {
192 for contact in &self.contacts {
193 let key = (contact.body_a.min(contact.body_b), contact.body_a.max(contact.body_b));
194 if self.frame_contact_pairs.insert(key) {
195 self.frame_contacts.push(contact.clone());
196 }
197 }
198 }
199
200 self.manifolds.sort_by(|a, b| {
202 let ay = self.bodies[a.body_a as usize]
203 .as_ref()
204 .map_or(0.0f32, |body| body.y)
205 .max(
206 self.bodies[a.body_b as usize]
207 .as_ref()
208 .map_or(0.0f32, |body| body.y),
209 );
210 let by = self.bodies[b.body_a as usize]
211 .as_ref()
212 .map_or(0.0f32, |body| body.y)
213 .max(
214 self.bodies[b.body_b as usize]
215 .as_ref()
216 .map_or(0.0f32, |body| body.y),
217 );
218 by.partial_cmp(&ay).unwrap_or(std::cmp::Ordering::Equal)
219 });
220
221 let gravity_mag = (self.gravity.0 * self.gravity.0 + self.gravity.1 * self.gravity.1).sqrt();
223 let restitution_threshold = gravity_mag * sub_dt * 1.5;
224 initialize_manifolds(&self.bodies, &mut self.manifolds, restitution_threshold);
225
226 for manifold in &mut self.manifolds {
228 let pair_key = (
229 manifold.body_a.min(manifold.body_b),
230 manifold.body_a.max(manifold.body_b),
231 );
232 for point in &mut manifold.points {
233 let key = (pair_key.0, pair_key.1, point.id);
234 if let Some(&(jn, jt)) = self.manifold_warm_cache.get(&key) {
235 point.accumulated_jn = jn * 0.95;
236 point.accumulated_jt = jt * 0.95;
237 }
238 }
239 }
240 warm_start_manifolds(&mut self.bodies, &self.manifolds);
241
242 for constraint in &mut self.constraints {
244 match constraint {
245 Constraint::Distance { soft: Some(_), accumulated_impulse, .. } => {
246 *accumulated_impulse = 0.0;
247 }
248 Constraint::Revolute { soft: Some(_), accumulated_impulse, .. } => {
249 *accumulated_impulse = (0.0, 0.0);
250 }
251 _ => {}
252 }
253 }
254
255 for i in 0..self.solver_iterations {
257 let reverse = i % 2 == 1;
258 resolve_manifolds_velocity_iteration(&mut self.bodies, &mut self.manifolds, reverse, sub_dt);
259 solve_constraints(&mut self.bodies, &mut self.constraints, sub_dt);
260 }
261
262 self.manifold_warm_cache.clear();
264 for manifold in &self.manifolds {
265 let pair_key = (
266 manifold.body_a.min(manifold.body_b),
267 manifold.body_a.max(manifold.body_b),
268 );
269 for point in &manifold.points {
270 let key = (pair_key.0, pair_key.1, point.id);
271 self.manifold_warm_cache.insert(key, (point.accumulated_jn, point.accumulated_jt));
272 }
273 }
274
275 for i in 0..3 {
279 for manifold in &mut self.manifolds {
280 let a = &self.bodies[manifold.body_a as usize];
281 let b = &self.bodies[manifold.body_b as usize];
282 if let (Some(a), Some(b)) = (a, b) {
283 if let Some(fresh) = test_collision_manifold_speculative(a, b, SPECULATIVE_MARGIN) {
284 manifold.normal = fresh.normal;
285 manifold.tangent = fresh.tangent;
286 for (point, fresh_point) in manifold.points.iter_mut().zip(fresh.points.iter()) {
287 point.penetration = fresh_point.penetration;
288 point.local_a = fresh_point.local_a;
289 point.local_b = fresh_point.local_b;
290 }
291 if manifold.points.len() != fresh.points.len() {
292 manifold.points = fresh.points;
293 }
294 } else {
295 for point in &mut manifold.points {
296 point.penetration = 0.0;
297 }
298 }
299 }
300 }
301 resolve_manifolds_position(&mut self.bodies, &self.manifolds, i % 2 == 1);
302 solve_constraints_position(&mut self.bodies, &self.constraints);
303 }
304
305 for manifold in &self.manifolds {
307 let a_idx = manifold.body_a as usize;
308 let b_idx = manifold.body_b as usize;
309
310 let still_penetrating = manifold.points.iter().any(|p| p.penetration > 0.01);
311 if !still_penetrating {
312 continue;
313 }
314
315 let (nx, ny) = manifold.normal;
316
317 if let Some(a) = &mut self.bodies[a_idx] {
318 if a.body_type == BodyType::Dynamic {
319 let vn = a.vx * nx + a.vy * ny;
320 if vn > 0.0 {
321 a.vx -= vn * nx;
322 a.vy -= vn * ny;
323 }
324 }
325 }
326 if let Some(b) = &mut self.bodies[b_idx] {
327 if b.body_type == BodyType::Dynamic {
328 let vn = b.vx * nx + b.vy * ny;
329 if vn < 0.0 {
330 b.vx -= vn * nx;
331 b.vy -= vn * ny;
332 }
333 }
334 }
335 }
336 }
337
338 update_sleep(&mut self.bodies, &self.contacts, fixed_dt);
340 }
341
342 #[allow(dead_code)]
349 fn update_contacts_analytically(&mut self) {
350 for manifold in &mut self.manifolds {
351 let a = match &self.bodies[manifold.body_a as usize] {
352 Some(b) => b,
353 None => continue,
354 };
355 let b = match &self.bodies[manifold.body_b as usize] {
356 Some(b) => b,
357 None => continue,
358 };
359
360 for point in &mut manifold.points {
361 let cos_a = a.angle.cos();
363 let sin_a = a.angle.sin();
364 let wa_x = point.local_a.0 * cos_a - point.local_a.1 * sin_a + a.x;
365 let wa_y = point.local_a.0 * sin_a + point.local_a.1 * cos_a + a.y;
366
367 let cos_b = b.angle.cos();
368 let sin_b = b.angle.sin();
369 let wb_x = point.local_b.0 * cos_b - point.local_b.1 * sin_b + b.x;
370 let wb_y = point.local_b.0 * sin_b + point.local_b.1 * cos_b + b.y;
371
372 let dx = wb_x - wa_x;
375 let dy = wb_y - wa_y;
376 point.penetration = -(dx * manifold.normal.0 + dy * manifold.normal.1);
377 }
378 }
379 }
380
381 fn sub_step_legacy(&mut self, dt: f32) {
383 for body in self.bodies.iter_mut().flatten() {
385 integrate(body, self.gravity.0, self.gravity.1, dt);
386 }
387
388 self.broadphase.clear();
390 for body in self.bodies.iter().flatten() {
391 let (min_x, min_y, max_x, max_y) = get_shape_aabb(body);
392 self.broadphase.insert(body.id, min_x, min_y, max_x, max_y);
393 }
394 let pairs = self.broadphase.get_pairs();
395
396 self.contacts.clear();
398 for (id_a, id_b) in pairs {
399 let a_idx = id_a as usize;
400 let b_idx = id_b as usize;
401
402 let (layer_a, mask_a, sleeping_a) = match &self.bodies[a_idx] {
404 Some(b) => (b.layer, b.mask, b.sleeping),
405 None => continue,
406 };
407 let (layer_b, mask_b, sleeping_b) = match &self.bodies[b_idx] {
408 Some(b) => (b.layer, b.mask, b.sleeping),
409 None => continue,
410 };
411
412 if (layer_a & mask_b) == 0 || (layer_b & mask_a) == 0 {
414 continue;
415 }
416
417 if sleeping_a && sleeping_b {
419 continue;
420 }
421
422 let body_a = self.bodies[a_idx].as_ref().unwrap();
424 let body_b = self.bodies[b_idx].as_ref().unwrap();
425
426 if let Some(contact) = test_collision(body_a, body_b) {
427 self.contacts.push(contact);
428 }
429 }
430
431 self.contacts.sort_by(|a, b| {
433 let ay = self.bodies[a.body_a as usize]
434 .as_ref()
435 .map_or(0.0f32, |body| body.y)
436 .max(
437 self.bodies[a.body_b as usize]
438 .as_ref()
439 .map_or(0.0f32, |body| body.y),
440 );
441 let by = self.bodies[b.body_a as usize]
442 .as_ref()
443 .map_or(0.0f32, |body| body.y)
444 .max(
445 self.bodies[b.body_b as usize]
446 .as_ref()
447 .map_or(0.0f32, |body| body.y),
448 );
449 by.partial_cmp(&ay).unwrap_or(std::cmp::Ordering::Equal)
450 });
451
452 let gravity_mag = (self.gravity.0 * self.gravity.0 + self.gravity.1 * self.gravity.1).sqrt();
455 let restitution_threshold = gravity_mag * dt * 1.5;
456 initialize_contacts(&self.bodies, &mut self.contacts, restitution_threshold);
457
458 for contact in &mut self.contacts {
460 let key = (contact.body_a.min(contact.body_b), contact.body_a.max(contact.body_b));
461 if let Some(&(jn, jt)) = self.warm_cache.get(&key) {
462 contact.accumulated_jn = jn * 0.95;
466 contact.accumulated_jt = jt * 0.95;
467 }
468 }
469 warm_start_contacts(&mut self.bodies, &self.contacts);
470
471 for constraint in &mut self.constraints {
475 match constraint {
476 Constraint::Distance { soft: Some(_), accumulated_impulse, .. } => {
477 *accumulated_impulse = 0.0;
478 }
479 Constraint::Revolute { soft: Some(_), accumulated_impulse, .. } => {
480 *accumulated_impulse = (0.0, 0.0);
481 }
482 _ => {}
483 }
484 }
485
486 for i in 0..self.solver_iterations {
488 let reverse = i % 2 == 1;
489 resolve_contacts_velocity_iteration(&mut self.bodies, &mut self.contacts, reverse);
490 solve_constraints(&mut self.bodies, &mut self.constraints, dt);
491 }
492
493 self.warm_cache.clear();
495 for contact in &self.contacts {
496 let key = (contact.body_a.min(contact.body_b), contact.body_a.max(contact.body_b));
497 self.warm_cache.insert(key, (contact.accumulated_jn, contact.accumulated_jt));
498 }
499
500 for i in 0..3 {
505 for contact in &mut self.contacts {
506 let a = &self.bodies[contact.body_a as usize];
507 let b = &self.bodies[contact.body_b as usize];
508 if let (Some(a), Some(b)) = (a, b) {
509 if let Some(fresh) = test_collision(a, b) {
510 contact.penetration = fresh.penetration;
511 contact.normal = fresh.normal;
512 contact.contact_point = fresh.contact_point;
513 } else {
514 contact.penetration = 0.0;
515 }
516 }
517 }
518 resolve_contacts_position(&mut self.bodies, &self.contacts, i % 2 == 1);
519 solve_constraints_position(&mut self.bodies, &self.constraints);
520 }
521
522 for contact in &self.contacts {
530 let a_idx = contact.body_a as usize;
531 let b_idx = contact.body_b as usize;
532
533 let still_penetrating = match (&self.bodies[a_idx], &self.bodies[b_idx]) {
535 (Some(a), Some(b)) => test_collision(a, b).map_or(false, |c| c.penetration > 0.01),
536 _ => false,
537 };
538
539 if !still_penetrating {
540 continue;
541 }
542
543 let (nx, ny) = contact.normal;
544
545 if let Some(a) = &mut self.bodies[a_idx] {
547 if a.body_type == BodyType::Dynamic {
548 let vn = a.vx * nx + a.vy * ny;
549 if vn > 0.0 {
550 a.vx -= vn * nx;
551 a.vy -= vn * ny;
552 }
553 }
554 }
555 if let Some(b) = &mut self.bodies[b_idx] {
557 if b.body_type == BodyType::Dynamic {
558 let vn = b.vx * nx + b.vy * ny;
559 if vn < 0.0 {
560 b.vx -= vn * nx;
561 b.vy -= vn * ny;
562 }
563 }
564 }
565 }
566
567 update_sleep(&mut self.bodies, &self.contacts, dt);
569 }
570
571 pub fn add_body(
572 &mut self,
573 body_type: BodyType,
574 shape: Shape,
575 x: f32,
576 y: f32,
577 mass: f32,
578 material: Material,
579 layer: u16,
580 mask: u16,
581 ) -> BodyId {
582 let id = if let Some(recycled) = self.free_ids.pop() {
583 recycled
584 } else {
585 let id = self.next_id;
586 self.next_id += 1;
587 id
588 };
589
590 let (inv_mass, inertia, inv_inertia) = compute_mass_and_inertia(&shape, mass, body_type);
591
592 let body = RigidBody {
593 id,
594 body_type,
595 shape,
596 material,
597 x,
598 y,
599 angle: 0.0,
600 vx: 0.0,
601 vy: 0.0,
602 angular_velocity: 0.0,
603 fx: 0.0,
604 fy: 0.0,
605 torque: 0.0,
606 mass,
607 inv_mass,
608 inertia,
609 inv_inertia,
610 layer,
611 mask,
612 sleeping: false,
613 sleep_timer: 0.0,
614 };
615
616 let idx = id as usize;
617 if idx >= self.bodies.len() {
618 self.bodies.resize_with(idx + 1, || None);
619 }
620 self.bodies[idx] = Some(body);
621 id
622 }
623
624 pub fn remove_body(&mut self, id: BodyId) {
625 let idx = id as usize;
626 if idx < self.bodies.len() {
627 self.bodies[idx] = None;
628 self.free_ids.push(id);
629 }
630 }
631
632 pub fn get_body(&self, id: BodyId) -> Option<&RigidBody> {
633 self.bodies.get(id as usize)?.as_ref()
634 }
635
636 pub fn get_body_mut(&mut self, id: BodyId) -> Option<&mut RigidBody> {
637 self.bodies.get_mut(id as usize)?.as_mut()
638 }
639
640 pub fn set_velocity(&mut self, id: BodyId, vx: f32, vy: f32) {
641 if let Some(body) = self.get_body_mut(id) {
642 body.vx = vx;
643 body.vy = vy;
644 body.sleeping = false;
645 body.sleep_timer = 0.0;
646 }
647 }
648
649 pub fn set_angular_velocity(&mut self, id: BodyId, av: f32) {
650 if let Some(body) = self.get_body_mut(id) {
651 body.angular_velocity = av;
652 body.sleeping = false;
653 body.sleep_timer = 0.0;
654 }
655 }
656
657 pub fn apply_force(&mut self, id: BodyId, fx: f32, fy: f32) {
658 if let Some(body) = self.get_body_mut(id) {
659 body.fx += fx;
660 body.fy += fy;
661 body.sleeping = false;
662 body.sleep_timer = 0.0;
663 }
664 }
665
666 pub fn apply_impulse(&mut self, id: BodyId, ix: f32, iy: f32) {
667 if let Some(body) = self.get_body_mut(id) {
668 body.vx += ix * body.inv_mass;
669 body.vy += iy * body.inv_mass;
670 body.sleeping = false;
671 body.sleep_timer = 0.0;
672 }
673 }
674
675 pub fn set_position(&mut self, id: BodyId, x: f32, y: f32) {
676 if let Some(body) = self.get_body_mut(id) {
677 body.x = x;
678 body.y = y;
679 body.sleeping = false;
680 body.sleep_timer = 0.0;
681 }
682 }
683
684 pub fn set_collision_layers(&mut self, id: BodyId, layer: u16, mask: u16) {
685 if let Some(body) = self.get_body_mut(id) {
686 body.layer = layer;
687 body.mask = mask;
688 }
689 }
690
691 pub fn add_constraint(&mut self, constraint: Constraint) -> ConstraintId {
692 let id = self.next_constraint_id;
693 self.next_constraint_id += 1;
694
695 let constraint = match constraint {
696 Constraint::Distance {
697 body_a,
698 body_b,
699 distance,
700 anchor_a,
701 anchor_b,
702 soft,
703 ..
704 } => Constraint::Distance {
705 id,
706 body_a,
707 body_b,
708 distance,
709 anchor_a,
710 anchor_b,
711 soft,
712 accumulated_impulse: 0.0,
713 },
714 Constraint::Revolute {
715 body_a,
716 body_b,
717 anchor_a,
718 anchor_b,
719 soft,
720 ..
721 } => Constraint::Revolute {
722 id,
723 body_a,
724 body_b,
725 anchor_a,
726 anchor_b,
727 soft,
728 accumulated_impulse: (0.0, 0.0),
729 },
730 };
731 self.constraints.push(constraint);
732 id
733 }
734
735 pub fn remove_constraint(&mut self, id: ConstraintId) {
736 self.constraints.retain(|c| c.id() != id);
737 }
738
739 pub fn query_aabb(&self, min_x: f32, min_y: f32, max_x: f32, max_y: f32) -> Vec<BodyId> {
740 let mut result = Vec::new();
741 for body in self.bodies.iter().flatten() {
742 let (bmin_x, bmin_y, bmax_x, bmax_y) = get_shape_aabb(body);
743 if bmax_x >= min_x && bmin_x <= max_x && bmax_y >= min_y && bmin_y <= max_y {
744 result.push(body.id);
745 }
746 }
747 result
748 }
749
750 pub fn raycast(
751 &self,
752 ox: f32,
753 oy: f32,
754 dx: f32,
755 dy: f32,
756 max_dist: f32,
757 ) -> Option<(BodyId, f32, f32, f32)> {
758 let dir_len = (dx * dx + dy * dy).sqrt();
759 if dir_len < 1e-8 {
760 return None;
761 }
762 let ndx = dx / dir_len;
763 let ndy = dy / dir_len;
764
765 let mut closest: Option<(BodyId, f32, f32, f32)> = None;
766
767 for body in self.bodies.iter().flatten() {
768 let t = match &body.shape {
769 Shape::Circle { radius } => {
770 ray_vs_circle(ox, oy, ndx, ndy, body.x, body.y, *radius)
771 }
772 Shape::AABB { half_w, half_h } => {
773 ray_vs_aabb(ox, oy, ndx, ndy, body.x, body.y, *half_w, *half_h)
774 }
775 Shape::Polygon { vertices } => {
776 ray_vs_polygon(ox, oy, ndx, ndy, body, vertices)
777 }
778 };
779
780 if let Some(t) = t {
781 if t >= 0.0 && t <= max_dist {
782 let hit_x = ox + ndx * t;
783 let hit_y = oy + ndy * t;
784 if closest.is_none() || t < closest.unwrap().3 {
785 closest = Some((body.id, hit_x, hit_y, t));
786 }
787 }
788 }
789 }
790 closest
791 }
792
793 pub fn get_contacts(&self) -> &[Contact] {
798 &self.frame_contacts
799 }
800
801 pub fn get_manifolds(&self) -> &[ContactManifold] {
805 &self.manifolds
806 }
807
808 pub fn all_bodies(&self) -> Vec<&RigidBody> {
810 self.bodies.iter().filter_map(|b| b.as_ref()).collect()
811 }
812
813 pub fn gravity(&self) -> (f32, f32) {
815 self.gravity
816 }
817
818 pub fn body_count(&self) -> usize {
820 self.bodies.iter().filter(|b| b.is_some()).count()
821 }
822}
823
824fn ray_vs_circle(
825 ox: f32, oy: f32,
826 dx: f32, dy: f32,
827 cx: f32, cy: f32,
828 radius: f32,
829) -> Option<f32> {
830 let fx = ox - cx;
831 let fy = oy - cy;
832 let a = dx * dx + dy * dy;
833 let b = 2.0 * (fx * dx + fy * dy);
834 let c = fx * fx + fy * fy - radius * radius;
835 let discriminant = b * b - 4.0 * a * c;
836 if discriminant < 0.0 {
837 return None;
838 }
839 let sqrt_d = discriminant.sqrt();
840 let t1 = (-b - sqrt_d) / (2.0 * a);
841 let t2 = (-b + sqrt_d) / (2.0 * a);
842 if t1 >= 0.0 {
843 Some(t1)
844 } else if t2 >= 0.0 {
845 Some(t2)
846 } else {
847 None
848 }
849}
850
851fn ray_vs_aabb(
852 ox: f32, oy: f32,
853 dx: f32, dy: f32,
854 cx: f32, cy: f32,
855 hw: f32, hh: f32,
856) -> Option<f32> {
857 let min_x = cx - hw;
858 let max_x = cx + hw;
859 let min_y = cy - hh;
860 let max_y = cy + hh;
861
862 let (mut tmin, mut tmax) = if dx.abs() < 1e-8 {
863 if ox < min_x || ox > max_x {
864 return None;
865 }
866 (f32::MIN, f32::MAX)
867 } else {
868 let inv_dx = 1.0 / dx;
869 let t1 = (min_x - ox) * inv_dx;
870 let t2 = (max_x - ox) * inv_dx;
871 (t1.min(t2), t1.max(t2))
872 };
873
874 let (tymin, tymax) = if dy.abs() < 1e-8 {
875 if oy < min_y || oy > max_y {
876 return None;
877 }
878 (f32::MIN, f32::MAX)
879 } else {
880 let inv_dy = 1.0 / dy;
881 let t1 = (min_y - oy) * inv_dy;
882 let t2 = (max_y - oy) * inv_dy;
883 (t1.min(t2), t1.max(t2))
884 };
885
886 tmin = tmin.max(tymin);
887 tmax = tmax.min(tymax);
888
889 if tmin > tmax || tmax < 0.0 {
890 return None;
891 }
892
893 Some(if tmin >= 0.0 { tmin } else { tmax })
894}
895
896fn ray_vs_polygon(
897 ox: f32, oy: f32,
898 dx: f32, dy: f32,
899 body: &RigidBody,
900 vertices: &[(f32, f32)],
901) -> Option<f32> {
902 let cos = body.angle.cos();
903 let sin = body.angle.sin();
904 let n = vertices.len();
905 if n < 3 {
906 return None;
907 }
908
909 let mut closest_t: Option<f32> = None;
910
911 for i in 0..n {
912 let (vx0, vy0) = vertices[i];
913 let (vx1, vy1) = vertices[(i + 1) % n];
914
915 let ax = vx0 * cos - vy0 * sin + body.x;
917 let ay = vx0 * sin + vy0 * cos + body.y;
918 let bx = vx1 * cos - vy1 * sin + body.x;
919 let by = vx1 * sin + vy1 * cos + body.y;
920
921 if let Some(t) = ray_vs_segment(ox, oy, dx, dy, ax, ay, bx, by) {
922 if closest_t.is_none() || t < closest_t.unwrap() {
923 closest_t = Some(t);
924 }
925 }
926 }
927 closest_t
928}
929
930fn ray_vs_segment(
931 ox: f32, oy: f32,
932 dx: f32, dy: f32,
933 ax: f32, ay: f32,
934 bx: f32, by: f32,
935) -> Option<f32> {
936 let ex = bx - ax;
937 let ey = by - ay;
938 let denom = dx * ey - dy * ex;
939 if denom.abs() < 1e-8 {
940 return None;
941 }
942 let t = ((ax - ox) * ey - (ay - oy) * ex) / denom;
943 let u = ((ax - ox) * dy - (ay - oy) * dx) / denom;
944 if t >= 0.0 && u >= 0.0 && u <= 1.0 {
945 Some(t)
946 } else {
947 None
948 }
949}