1use crate::math::{Fix128, Vec3Fix};
8
9#[cfg(not(feature = "std"))]
10use alloc::collections::{BTreeMap, BTreeSet};
11#[cfg(not(feature = "std"))]
12use alloc::vec::Vec;
13#[cfg(feature = "std")]
14use std::collections::{BTreeMap, BTreeSet};
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub enum ContactEventType {
19 Begin,
21 Persist,
23 End,
25}
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub struct ContactEvent {
30 pub body_a: usize,
32 pub body_b: usize,
34 pub event_type: ContactEventType,
36 pub normal: Vec3Fix,
41 pub point: Vec3Fix,
43 pub depth: Fix128,
45 pub relative_velocity: Fix128,
48}
49
50#[derive(Clone, Copy, Debug, PartialEq, Eq)]
52pub struct TriggerEvent {
53 pub trigger_body: usize,
55 pub other_body: usize,
57 pub entered: bool,
59}
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq)]
64pub struct JointBreakEvent {
65 pub index: usize,
69 pub joint: crate::joint::Joint,
71 pub force: Fix128,
75}
76
77pub struct EventCollector {
79 pub(crate) contact_events: Vec<ContactEvent>,
81 pub(crate) trigger_events: Vec<TriggerEvent>,
83 pub(crate) joint_break_events: Vec<JointBreakEvent>,
85 pub(crate) prev_pairs: Vec<(usize, usize)>,
87 pub(crate) curr_pairs: BTreeSet<(usize, usize)>,
92 pub(crate) prev_triggers: Vec<((usize, usize), (usize, usize))>,
94 pub(crate) curr_triggers: BTreeMap<(usize, usize), (usize, usize)>,
96}
97
98impl EventCollector {
99 #[must_use]
101 pub const fn new() -> Self {
102 Self {
103 contact_events: Vec::new(),
104 trigger_events: Vec::new(),
105 joint_break_events: Vec::new(),
106 prev_pairs: Vec::new(),
107 curr_pairs: BTreeSet::new(),
108 prev_triggers: Vec::new(),
109 curr_triggers: BTreeMap::new(),
110 }
111 }
112
113 pub fn begin_frame(&mut self) {
115 self.contact_events.clear();
116 self.trigger_events.clear();
117 self.joint_break_events.clear();
118 self.prev_pairs.clear();
121 self.prev_pairs.extend(self.curr_pairs.iter().copied());
122 self.curr_pairs.clear();
123 self.prev_triggers.clear();
124 self.prev_triggers
125 .extend(self.curr_triggers.iter().map(|(k, v)| (*k, *v)));
126 self.curr_triggers.clear();
127 }
128
129 pub fn report_contact(
137 &mut self,
138 body_a: usize,
139 body_b: usize,
140 normal: Vec3Fix,
141 point: Vec3Fix,
142 depth: Fix128,
143 relative_velocity: Fix128,
144 ) {
145 let pair = normalize_pair(body_a, body_b);
146 let normal = if pair.0 == body_a { normal } else { -normal };
148 let was_active = self.prev_pairs.binary_search(&pair).is_ok();
149 if !self.curr_pairs.insert(pair) {
152 return;
153 }
154
155 let event_type = if was_active {
156 ContactEventType::Persist
157 } else {
158 ContactEventType::Begin
159 };
160
161 self.contact_events.push(ContactEvent {
162 body_a: pair.0,
163 body_b: pair.1,
164 event_type,
165 normal,
166 point,
167 depth,
168 relative_velocity,
169 });
170 }
171
172 pub fn report_trigger(&mut self, trigger_body: usize, other_body: usize) {
174 let pair = normalize_pair(trigger_body, other_body);
175 if self.curr_triggers.contains_key(&pair) {
178 return;
179 }
180
181 let prev = self
182 .prev_triggers
183 .binary_search_by_key(&pair, |&(k, _)| k)
184 .ok()
185 .map(|i| self.prev_triggers[i].1);
186 self.curr_triggers
190 .insert(pair, prev.unwrap_or((trigger_body, other_body)));
191
192 if prev.is_none() {
193 self.trigger_events.push(TriggerEvent {
194 trigger_body,
195 other_body,
196 entered: true,
197 });
198 }
199 }
200
201 pub fn end_frame(&mut self) {
203 for &pair in &self.prev_pairs {
205 if !self.curr_pairs.contains(&pair) {
206 self.contact_events.push(ContactEvent {
207 body_a: pair.0,
208 body_b: pair.1,
209 event_type: ContactEventType::End,
210 normal: Vec3Fix::ZERO,
211 point: Vec3Fix::ZERO,
212 depth: Fix128::ZERO,
213 relative_velocity: Fix128::ZERO,
214 });
215 }
216 }
217
218 for &(pair, (trigger_body, other_body)) in &self.prev_triggers {
220 if !self.curr_triggers.contains_key(&pair) {
221 self.trigger_events.push(TriggerEvent {
224 trigger_body,
225 other_body,
226 entered: false,
227 });
228 }
229 }
230 }
231
232 #[inline]
234 #[must_use]
235 pub fn contact_events(&self) -> &[ContactEvent] {
236 &self.contact_events
237 }
238
239 #[inline]
241 #[must_use]
242 pub fn trigger_events(&self) -> &[TriggerEvent] {
243 &self.trigger_events
244 }
245
246 #[inline]
249 #[must_use]
250 pub fn joint_break_events(&self) -> &[JointBreakEvent] {
251 &self.joint_break_events
252 }
253
254 pub(crate) fn report_joint_break(&mut self, event: JointBreakEvent) {
256 self.joint_break_events.push(event);
257 }
258
259 #[inline]
261 pub fn drain_joint_break_events(&mut self) -> Vec<JointBreakEvent> {
262 core::mem::take(&mut self.joint_break_events)
263 }
264
265 #[inline]
267 pub fn drain_contact_events(&mut self) -> Vec<ContactEvent> {
268 core::mem::take(&mut self.contact_events)
269 }
270
271 #[inline]
273 pub fn drain_trigger_events(&mut self) -> Vec<TriggerEvent> {
274 core::mem::take(&mut self.trigger_events)
275 }
276
277 #[inline]
279 #[must_use]
280 pub fn has_events(&self) -> bool {
281 !self.contact_events.is_empty()
282 || !self.trigger_events.is_empty()
283 || !self.joint_break_events.is_empty()
284 }
285}
286
287impl Default for EventCollector {
288 fn default() -> Self {
289 Self::new()
290 }
291}
292
293#[inline]
295const fn normalize_pair(a: usize, b: usize) -> (usize, usize) {
296 if a <= b {
297 (a, b)
298 } else {
299 (b, a)
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306
307 #[test]
312 fn step_reports_a_broken_joint_with_its_reaction_force() {
313 use crate::joint::{BallJoint, Joint};
314 use crate::solver::{PhysicsConfig, PhysicsWorld, RigidBody};
315 let config = PhysicsConfig {
316 substeps: 1,
317 gravity: Vec3Fix::new(Fix128::ZERO, Fix128::from_int(-8), Fix128::ZERO),
318 damping: Fix128::ONE,
319 ..PhysicsConfig::default()
320 };
321 let mut w = PhysicsWorld::new(config);
322 w.add_body(RigidBody::new_static(Vec3Fix::ZERO));
323 w.add_body(RigidBody::new(Vec3Fix::ZERO, Fix128::from_int(2)));
324 let joint = Joint::Ball(
325 BallJoint::new(0, 1, Vec3Fix::ZERO, Vec3Fix::ZERO)
326 .with_break_force(Fix128::from_int(15)),
327 );
328 w.add_joint(joint);
329 w.step(Fix128::from_ratio(1, 64));
330 assert!(w.joints.is_empty());
331 assert!(w.events.has_events());
332 assert_eq!(
333 w.events.joint_break_events(),
334 &[JointBreakEvent {
335 index: 0,
336 joint,
337 force: Fix128::from_int(16)
338 }]
339 );
340 w.step(Fix128::from_ratio(1, 64));
342 assert!(w.events.joint_break_events().is_empty());
343 }
344
345 #[test]
349 fn joint_break_events_live_for_one_frame() {
350 use crate::joint::{BallJoint, Joint};
351 let event = JointBreakEvent {
352 index: 3,
353 joint: Joint::Ball(BallJoint::new(1, 2, Vec3Fix::ZERO, Vec3Fix::UNIT_Y)),
354 force: Fix128::from_int(7),
355 };
356 let mut events = EventCollector::default();
357 events.begin_frame();
358 assert!(!events.has_events());
359 events.report_joint_break(event);
360 events.report_joint_break(JointBreakEvent { index: 1, ..event });
361 assert!(events.has_events(), "a joint break alone is an event");
362 assert_eq!(events.joint_break_events().len(), 2);
363 assert_eq!(events.joint_break_events()[1].index, 1);
364 assert_eq!(
365 events.drain_joint_break_events(),
366 vec![event, JointBreakEvent { index: 1, ..event }]
367 );
368 assert!(events.joint_break_events().is_empty() && !events.has_events());
369 events.report_joint_break(event);
370 events.begin_frame();
371 assert!(
372 events.joint_break_events().is_empty(),
373 "begin_frame clears them"
374 );
375 }
376
377 #[test]
378 fn test_contact_begin() {
379 let mut events = EventCollector::new();
380 events.begin_frame();
381 events.report_contact(
382 0,
383 1,
384 Vec3Fix::UNIT_Y,
385 Vec3Fix::ZERO,
386 Fix128::ONE,
387 Fix128::ZERO,
388 );
389 events.end_frame();
390
391 assert_eq!(events.contact_events().len(), 1);
392 assert_eq!(
393 events.contact_events()[0].event_type,
394 ContactEventType::Begin
395 );
396 }
397
398 #[test]
399 fn test_contact_persist() {
400 let mut events = EventCollector::new();
401
402 events.begin_frame();
404 events.report_contact(
405 0,
406 1,
407 Vec3Fix::UNIT_Y,
408 Vec3Fix::ZERO,
409 Fix128::ONE,
410 Fix128::ZERO,
411 );
412 events.end_frame();
413
414 events.begin_frame();
416 events.report_contact(
417 0,
418 1,
419 Vec3Fix::UNIT_Y,
420 Vec3Fix::ZERO,
421 Fix128::ONE,
422 Fix128::ZERO,
423 );
424 events.end_frame();
425
426 assert_eq!(
427 events.contact_events()[0].event_type,
428 ContactEventType::Persist
429 );
430 }
431
432 #[test]
433 fn test_contact_end() {
434 let mut events = EventCollector::new();
435
436 events.begin_frame();
438 events.report_contact(
439 0,
440 1,
441 Vec3Fix::UNIT_Y,
442 Vec3Fix::ZERO,
443 Fix128::ONE,
444 Fix128::ZERO,
445 );
446 events.end_frame();
447
448 events.begin_frame();
450 events.end_frame();
451
452 let end_events: Vec<_> = events
453 .contact_events()
454 .iter()
455 .filter(|e| e.event_type == ContactEventType::End)
456 .collect();
457 assert_eq!(end_events.len(), 1);
458 assert_eq!(end_events[0].body_a, 0);
459 assert_eq!(end_events[0].body_b, 1);
460 }
461
462 #[test]
463 fn test_trigger_enter_exit() {
464 let mut events = EventCollector::new();
465
466 events.begin_frame();
468 events.report_trigger(0, 1);
469 events.end_frame();
470
471 assert_eq!(events.trigger_events().len(), 1);
472 assert!(events.trigger_events()[0].entered);
473
474 events.begin_frame();
476 events.report_trigger(0, 1);
477 events.end_frame();
478
479 let enters = events.trigger_events().iter().filter(|e| e.entered).count();
481 assert_eq!(enters, 0);
482
483 events.begin_frame();
485 events.end_frame();
486
487 let exits = events
488 .trigger_events()
489 .iter()
490 .filter(|e| !e.entered)
491 .count();
492 assert_eq!(exits, 1);
493 }
494
495 #[test]
496 fn test_pair_normalization() {
497 let mut events = EventCollector::new();
498 events.begin_frame();
499 events.report_contact(
501 3,
502 1,
503 Vec3Fix::UNIT_Y,
504 Vec3Fix::ZERO,
505 Fix128::ONE,
506 Fix128::ZERO,
507 );
508 events.end_frame();
509
510 assert_eq!(events.contact_events()[0].body_a, 1);
512 assert_eq!(events.contact_events()[0].body_b, 3);
513 }
514
515 #[test]
516 fn has_events_tracks_contact_and_trigger_queues_across_frame_lifecycle() {
517 let mut events = EventCollector::new();
518 assert!(!events.has_events());
519 events.begin_frame();
520 assert!(!events.has_events());
521
522 events.report_contact(
524 0,
525 1,
526 Vec3Fix::UNIT_Y,
527 Vec3Fix::ZERO,
528 Fix128::ONE,
529 Fix128::ZERO,
530 );
531 assert!(events.has_events());
532 assert_eq!(events.drain_contact_events().len(), 1);
533 assert!(!events.has_events());
534
535 events.report_trigger(5, 6);
537 assert!(events.has_events());
538 assert!(events.contact_events().is_empty());
539 assert_eq!(events.drain_trigger_events().len(), 1);
540 assert!(!events.has_events());
541
542 events.report_contact(
544 1,
545 0,
546 Vec3Fix::UNIT_Y,
547 Vec3Fix::ZERO,
548 Fix128::ONE,
549 Fix128::ZERO,
550 );
551 assert!(!events.has_events());
552 events.end_frame();
553 assert!(!events.has_events(), "全 pair 継続中なので End/exit なし");
554
555 events.begin_frame();
557 assert!(!events.has_events(), "begin_frame は queue を空にする");
558 events.end_frame();
559 assert!(events.has_events());
560 assert_eq!(events.contact_events().len(), 1);
561 assert_eq!(events.contact_events()[0].event_type, ContactEventType::End);
562 assert_eq!(events.trigger_events().len(), 1);
563 assert!(!events.trigger_events()[0].entered);
564
565 events.begin_frame();
567 events.end_frame();
568 assert!(!events.has_events());
569 }
570}