oxiphysics_python/world_api/
geometry.rs1#![allow(missing_docs)]
7
8#[derive(Debug, Clone, Copy)]
14#[allow(dead_code)]
15pub struct PyAabb {
16 pub min: [f64; 3],
18 pub max: [f64; 3],
20}
21
22impl PyAabb {
23 pub fn new(min: [f64; 3], max: [f64; 3]) -> Self {
25 Self { min, max }
26 }
27
28 pub fn unit() -> Self {
30 Self::new([-0.5; 3], [0.5; 3])
31 }
32
33 pub fn from_center_half_extents(center: [f64; 3], he: [f64; 3]) -> Self {
35 Self {
36 min: [center[0] - he[0], center[1] - he[1], center[2] - he[2]],
37 max: [center[0] + he[0], center[1] + he[1], center[2] + he[2]],
38 }
39 }
40
41 pub fn center(&self) -> [f64; 3] {
43 [
44 (self.min[0] + self.max[0]) * 0.5,
45 (self.min[1] + self.max[1]) * 0.5,
46 (self.min[2] + self.max[2]) * 0.5,
47 ]
48 }
49
50 pub fn half_extents(&self) -> [f64; 3] {
52 [
53 (self.max[0] - self.min[0]) * 0.5,
54 (self.max[1] - self.min[1]) * 0.5,
55 (self.max[2] - self.min[2]) * 0.5,
56 ]
57 }
58
59 pub fn surface_area(&self) -> f64 {
61 let dx = self.max[0] - self.min[0];
62 let dy = self.max[1] - self.min[1];
63 let dz = self.max[2] - self.min[2];
64 2.0 * (dx * dy + dy * dz + dz * dx)
65 }
66
67 pub fn volume(&self) -> f64 {
69 let dx = (self.max[0] - self.min[0]).max(0.0);
70 let dy = (self.max[1] - self.min[1]).max(0.0);
71 let dz = (self.max[2] - self.min[2]).max(0.0);
72 dx * dy * dz
73 }
74
75 pub fn contains_point(&self, p: [f64; 3]) -> bool {
77 p[0] >= self.min[0]
78 && p[0] <= self.max[0]
79 && p[1] >= self.min[1]
80 && p[1] <= self.max[1]
81 && p[2] >= self.min[2]
82 && p[2] <= self.max[2]
83 }
84
85 pub fn intersects(&self, other: &PyAabb) -> bool {
87 self.min[0] <= other.max[0]
88 && self.max[0] >= other.min[0]
89 && self.min[1] <= other.max[1]
90 && self.max[1] >= other.min[1]
91 && self.min[2] <= other.max[2]
92 && self.max[2] >= other.min[2]
93 }
94
95 pub fn merged(&self, other: &PyAabb) -> PyAabb {
97 PyAabb {
98 min: [
99 self.min[0].min(other.min[0]),
100 self.min[1].min(other.min[1]),
101 self.min[2].min(other.min[2]),
102 ],
103 max: [
104 self.max[0].max(other.max[0]),
105 self.max[1].max(other.max[1]),
106 self.max[2].max(other.max[2]),
107 ],
108 }
109 }
110}
111
112#[derive(Debug, Clone, Copy)]
114#[allow(dead_code)]
115pub struct PySphere {
116 pub center: [f64; 3],
118 pub radius: f64,
120}
121
122impl PySphere {
123 pub fn new(center: [f64; 3], radius: f64) -> Self {
125 Self {
126 center,
127 radius: radius.max(0.0),
128 }
129 }
130
131 pub fn unit() -> Self {
133 Self::new([0.0; 3], 1.0)
134 }
135
136 pub fn surface_area(&self) -> f64 {
138 4.0 * std::f64::consts::PI * self.radius * self.radius
139 }
140
141 pub fn volume(&self) -> f64 {
143 (4.0 / 3.0) * std::f64::consts::PI * self.radius * self.radius * self.radius
144 }
145
146 pub fn contains_point(&self, p: [f64; 3]) -> bool {
148 let dx = p[0] - self.center[0];
149 let dy = p[1] - self.center[1];
150 let dz = p[2] - self.center[2];
151 dx * dx + dy * dy + dz * dz <= self.radius * self.radius
152 }
153
154 pub fn signed_distance(&self, p: [f64; 3]) -> f64 {
158 let dx = p[0] - self.center[0];
159 let dy = p[1] - self.center[1];
160 let dz = p[2] - self.center[2];
161 (dx * dx + dy * dy + dz * dz).sqrt() - self.radius
162 }
163
164 pub fn overlaps(&self, other: &PySphere) -> bool {
166 let dx = self.center[0] - other.center[0];
167 let dy = self.center[1] - other.center[1];
168 let dz = self.center[2] - other.center[2];
169 let dist2 = dx * dx + dy * dy + dz * dz;
170 let sum_r = self.radius + other.radius;
171 dist2 <= sum_r * sum_r
172 }
173
174 pub fn aabb(&self) -> PyAabb {
176 PyAabb::from_center_half_extents(self.center, [self.radius; 3])
177 }
178}
179
180#[derive(Debug, Clone)]
185#[allow(dead_code)]
186pub struct PyConvexHull {
187 pub vertices: Vec<[f64; 3]>,
189}
190
191impl PyConvexHull {
192 pub fn new(vertices: Vec<[f64; 3]>) -> Self {
194 Self { vertices }
195 }
196
197 pub fn unit_cube() -> Self {
199 let verts: Vec<[f64; 3]> = [
200 [-0.5, -0.5, -0.5],
201 [0.5, -0.5, -0.5],
202 [0.5, 0.5, -0.5],
203 [-0.5, 0.5, -0.5],
204 [-0.5, -0.5, 0.5],
205 [0.5, -0.5, 0.5],
206 [0.5, 0.5, 0.5],
207 [-0.5, 0.5, 0.5],
208 ]
209 .to_vec();
210 Self::new(verts)
211 }
212
213 pub fn vertex_count(&self) -> usize {
215 self.vertices.len()
216 }
217
218 pub fn aabb(&self) -> Option<PyAabb> {
220 if self.vertices.is_empty() {
221 return None;
222 }
223 let mut mn = self.vertices[0];
224 let mut mx = self.vertices[0];
225 for &v in &self.vertices {
226 for k in 0..3 {
227 mn[k] = mn[k].min(v[k]);
228 mx[k] = mx[k].max(v[k]);
229 }
230 }
231 Some(PyAabb::new(mn, mx))
232 }
233
234 pub fn centroid(&self) -> Option<[f64; 3]> {
236 if self.vertices.is_empty() {
237 return None;
238 }
239 let n = self.vertices.len() as f64;
240 let mut c = [0.0f64; 3];
241 for v in &self.vertices {
242 c[0] += v[0];
243 c[1] += v[1];
244 c[2] += v[2];
245 }
246 Some([c[0] / n, c[1] / n, c[2] / n])
247 }
248
249 pub fn may_contain_point(&self, p: [f64; 3]) -> bool {
252 match self.aabb() {
253 Some(aabb) => aabb.contains_point(p),
254 None => false,
255 }
256 }
257
258 pub fn support(&self, d: [f64; 3]) -> Option<[f64; 3]> {
260 self.vertices
261 .iter()
262 .max_by(|a, b| {
263 let da = a[0] * d[0] + a[1] * d[1] + a[2] * d[2];
264 let db = b[0] * d[0] + b[1] * d[1] + b[2] * d[2];
265 da.partial_cmp(&db).unwrap_or(std::cmp::Ordering::Equal)
266 })
267 .copied()
268 }
269}