oxiphysics_python/world_api/
stats.rs1#![allow(missing_docs)]
7
8use super::PyPhysicsWorld;
9
10#[derive(Debug, Clone, Default)]
18#[allow(dead_code)]
19pub struct SimStats {
20 pub body_count: usize,
22 pub sleeping_count: usize,
24 pub awake_count: usize,
26 pub contact_count: usize,
28 pub simulation_time: f64,
30 pub total_kinetic_energy: f64,
32 pub max_linear_speed: f64,
34 pub max_angular_speed: f64,
36}
37
38impl PyPhysicsWorld {
39 pub fn stats(&self) -> SimStats {
41 let mut s = SimStats {
42 body_count: self.body_count(),
43 sleeping_count: self.sleeping_count(),
44 contact_count: self.contacts.len(),
45 simulation_time: self.time,
46 ..SimStats::default()
47 };
48 s.awake_count = s.body_count.saturating_sub(s.sleeping_count);
49
50 for slot in &self.slots {
51 if let Some(body) = slot.body.as_ref() {
52 if body.is_static || body.is_kinematic {
53 continue;
54 }
55 let v2 =
56 body.velocity[0].powi(2) + body.velocity[1].powi(2) + body.velocity[2].powi(2);
57 let speed = v2.sqrt();
58 s.total_kinetic_energy += 0.5 * body.mass * v2;
59 if speed > s.max_linear_speed {
60 s.max_linear_speed = speed;
61 }
62 let w = (body.angular_velocity[0].powi(2)
63 + body.angular_velocity[1].powi(2)
64 + body.angular_velocity[2].powi(2))
65 .sqrt();
66 if w > s.max_angular_speed {
67 s.max_angular_speed = w;
68 }
69 }
70 }
71 s
72 }
73
74 pub fn body_kinetic_energy(&self, handle: u32) -> Option<f64> {
76 let body = self.get_body(handle)?;
77 let v2 = body.velocity[0].powi(2) + body.velocity[1].powi(2) + body.velocity[2].powi(2);
78 Some(0.5 * body.mass * v2)
79 }
80
81 pub fn closest_body(&self, point: [f64; 3]) -> Option<(u32, f64)> {
83 let mut best_handle: Option<u32> = None;
84 let mut best_dist = f64::MAX;
85 for (i, slot) in self.slots.iter().enumerate() {
86 if let Some(body) = slot.body.as_ref() {
87 let dx = body.position[0] - point[0];
88 let dy = body.position[1] - point[1];
89 let dz = body.position[2] - point[2];
90 let dist = (dx * dx + dy * dy + dz * dz).sqrt();
91 if dist < best_dist {
92 best_dist = dist;
93 best_handle = Some(i as u32);
94 }
95 }
96 }
97 best_handle.map(|h| (h, best_dist))
98 }
99
100 pub fn bodies_in_sphere(&self, center: [f64; 3], radius: f64) -> Vec<u32> {
102 let r2 = radius * radius;
103 self.slots
104 .iter()
105 .enumerate()
106 .filter_map(|(i, slot)| {
107 let body = slot.body.as_ref()?;
108 let dx = body.position[0] - center[0];
109 let dy = body.position[1] - center[1];
110 let dz = body.position[2] - center[2];
111 if dx * dx + dy * dy + dz * dz <= r2 {
112 Some(i as u32)
113 } else {
114 None
115 }
116 })
117 .collect()
118 }
119
120 pub fn closest_pair(&self) -> Option<(u32, u32, f64)> {
124 let active: Vec<(u32, [f64; 3])> = self
125 .slots
126 .iter()
127 .enumerate()
128 .filter_map(|(i, s)| s.body.as_ref().map(|b| (i as u32, b.position)))
129 .collect();
130 if active.len() < 2 {
131 return None;
132 }
133 let mut best = (0u32, 0u32, f64::MAX);
134 for i in 0..active.len() {
135 for j in (i + 1)..active.len() {
136 let (h_a, p_a) = active[i];
137 let (h_b, p_b) = active[j];
138 let dx = p_a[0] - p_b[0];
139 let dy = p_a[1] - p_b[1];
140 let dz = p_a[2] - p_b[2];
141 let dist = (dx * dx + dy * dy + dz * dz).sqrt();
142 if dist < best.2 {
143 best = (h_a, h_b, dist);
144 }
145 }
146 }
147 Some(best)
148 }
149}