1use serde::{Deserialize, Serialize};
8
9#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
11#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
12#[serde(from = "[f64; 3]", into = "[f64; 3]")]
13pub struct Point3 {
14 pub x: f64,
15 pub y: f64,
16 pub z: f64,
17}
18
19pub type Vector3 = Point3;
21
22impl From<[f64; 3]> for Point3 {
23 fn from(v: [f64; 3]) -> Self {
24 Self { x: v[0], y: v[1], z: v[2] }
25 }
26}
27
28impl From<Point3> for [f64; 3] {
29 fn from(p: Point3) -> Self {
30 [p.x, p.y, p.z]
31 }
32}
33
34impl Point3 {
35 pub const ZERO: Point3 = Point3 { x: 0.0, y: 0.0, z: 0.0 };
36 pub const X: Point3 = Point3 { x: 1.0, y: 0.0, z: 0.0 };
37 pub const Y: Point3 = Point3 { x: 0.0, y: 1.0, z: 0.0 };
38 pub const Z: Point3 = Point3 { x: 0.0, y: 0.0, z: 1.0 };
39
40 pub const fn new(x: f64, y: f64, z: f64) -> Self {
41 Self { x, y, z }
42 }
43
44 pub fn is_finite(&self) -> bool {
45 self.x.is_finite() && self.y.is_finite() && self.z.is_finite()
46 }
47
48 pub fn add(self, o: Point3) -> Point3 {
49 Point3::new(self.x + o.x, self.y + o.y, self.z + o.z)
50 }
51
52 pub fn sub(self, o: Point3) -> Point3 {
53 Point3::new(self.x - o.x, self.y - o.y, self.z - o.z)
54 }
55
56 pub fn scale(self, s: f64) -> Point3 {
57 Point3::new(self.x * s, self.y * s, self.z * s)
58 }
59
60 pub fn dot(self, o: Point3) -> f64 {
61 self.x * o.x + self.y * o.y + self.z * o.z
62 }
63
64 pub fn cross(self, o: Point3) -> Point3 {
65 Point3::new(
66 self.y * o.z - self.z * o.y,
67 self.z * o.x - self.x * o.z,
68 self.x * o.y - self.y * o.x,
69 )
70 }
71
72 pub fn length_squared(self) -> f64 {
73 self.dot(self)
74 }
75
76 pub fn length(self) -> f64 {
77 self.length_squared().sqrt()
78 }
79
80 pub fn normalized(self) -> Option<Point3> {
82 let len = self.length();
83 if !(len > 0.0) || !len.is_finite() {
84 return None;
85 }
86 Some(self.scale(1.0 / len))
87 }
88
89 pub fn distance(self, o: Point3) -> f64 {
90 self.sub(o).length()
91 }
92
93 pub fn distance_xy(self, o: Point3) -> f64 {
95 let dx = self.x - o.x;
96 let dy = self.y - o.y;
97 (dx * dx + dy * dy).sqrt()
98 }
99
100 pub fn lerp(self, o: Point3, t: f64) -> Point3 {
101 self.add(o.sub(self).scale(t))
102 }
103
104 pub fn min(self, o: Point3) -> Point3 {
105 Point3::new(self.x.min(o.x), self.y.min(o.y), self.z.min(o.z))
106 }
107
108 pub fn max(self, o: Point3) -> Point3 {
109 Point3::new(self.x.max(o.x), self.y.max(o.y), self.z.max(o.z))
110 }
111
112 pub fn axis(self, axis: usize) -> f64 {
114 match axis {
115 0 => self.x,
116 1 => self.y,
117 _ => self.z,
118 }
119 }
120}
121
122#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
124#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
125#[serde(from = "[f64; 2]", into = "[f64; 2]")]
126pub struct Point2 {
127 pub x: f64,
128 pub y: f64,
129}
130
131impl From<[f64; 2]> for Point2 {
132 fn from(v: [f64; 2]) -> Self {
133 Self { x: v[0], y: v[1] }
134 }
135}
136
137impl From<Point2> for [f64; 2] {
138 fn from(p: Point2) -> Self {
139 [p.x, p.y]
140 }
141}
142
143impl Point2 {
144 pub const fn new(x: f64, y: f64) -> Self {
145 Self { x, y }
146 }
147
148 pub fn is_finite(&self) -> bool {
149 self.x.is_finite() && self.y.is_finite()
150 }
151}
152
153#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
155#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
156#[serde(rename_all = "camelCase")]
157pub struct Bounds3 {
158 pub min: Point3,
159 pub max: Point3,
160}
161
162impl Default for Bounds3 {
163 fn default() -> Self {
164 Self::empty()
165 }
166}
167
168impl Bounds3 {
169 pub fn empty() -> Self {
171 Self {
172 min: Point3::new(f64::INFINITY, f64::INFINITY, f64::INFINITY),
173 max: Point3::new(f64::NEG_INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY),
174 }
175 }
176
177 pub fn new(min: Point3, max: Point3) -> Self {
178 Self { min, max }
179 }
180
181 pub fn from_point(p: Point3) -> Self {
182 Self { min: p, max: p }
183 }
184
185 pub fn from_points<I: IntoIterator<Item = Point3>>(points: I) -> Self {
186 let mut b = Self::empty();
187 for p in points {
188 b.include(p);
189 }
190 b
191 }
192
193 pub fn is_empty(&self) -> bool {
194 self.min.x > self.max.x || self.min.y > self.max.y || self.min.z > self.max.z
195 }
196
197 pub fn is_finite(&self) -> bool {
198 self.min.is_finite() && self.max.is_finite()
199 }
200
201 pub fn include(&mut self, p: Point3) {
202 self.min = self.min.min(p);
203 self.max = self.max.max(p);
204 }
205
206 pub fn union(&self, o: &Bounds3) -> Bounds3 {
207 Bounds3 { min: self.min.min(o.min), max: self.max.max(o.max) }
208 }
209
210 pub fn expanded(&self, d: f64) -> Bounds3 {
212 let e = Point3::new(d, d, d);
213 Bounds3 { min: self.min.sub(e), max: self.max.add(e) }
214 }
215
216 pub fn intersects(&self, o: &Bounds3) -> bool {
218 self.min.x <= o.max.x
219 && o.min.x <= self.max.x
220 && self.min.y <= o.max.y
221 && o.min.y <= self.max.y
222 && self.min.z <= o.max.z
223 && o.min.z <= self.max.z
224 }
225
226 pub fn contains_point(&self, p: Point3) -> bool {
227 p.x >= self.min.x
228 && p.x <= self.max.x
229 && p.y >= self.min.y
230 && p.y <= self.max.y
231 && p.z >= self.min.z
232 && p.z <= self.max.z
233 }
234
235 pub fn center(&self) -> Point3 {
236 self.min.add(self.max).scale(0.5)
237 }
238
239 pub fn size(&self) -> Point3 {
240 self.max.sub(self.min)
241 }
242
243 pub fn diagonal(&self) -> f64 {
245 if self.is_empty() {
246 0.0
247 } else {
248 self.size().length()
249 }
250 }
251
252 pub fn longest_axis(&self) -> usize {
254 let s = self.size();
255 if s.x >= s.y && s.x >= s.z {
256 0
257 } else if s.y >= s.z {
258 1
259 } else {
260 2
261 }
262 }
263}
264
265#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
272#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
273#[serde(rename_all = "camelCase")]
274pub struct RigidTransform {
275 pub rotation: [[f64; 3]; 3],
276 pub translation: Point3,
277}
278
279impl Default for RigidTransform {
280 fn default() -> Self {
281 Self::identity()
282 }
283}
284
285#[derive(Clone, Debug, PartialEq, Eq)]
287pub enum TransformError {
288 NonFinite,
289 NotOrthonormal,
290 Mirrored,
291}
292
293impl std::fmt::Display for TransformError {
294 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
295 match self {
296 TransformError::NonFinite => write!(f, "rigid transform has a non-finite element"),
297 TransformError::NotOrthonormal => {
298 write!(f, "rigid transform rotation is not orthonormal")
299 }
300 TransformError::Mirrored => write!(f, "rigid transform mirrors (determinant is -1)"),
301 }
302 }
303}
304
305impl std::error::Error for TransformError {}
306
307impl RigidTransform {
308 pub const ORTHONORMAL_EPS: f64 = 1e-9;
310
311 pub fn identity() -> Self {
312 Self {
313 rotation: [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
314 translation: Point3::ZERO,
315 }
316 }
317
318 pub fn new(rotation: [[f64; 3]; 3], translation: Point3) -> Result<Self, TransformError> {
320 let t = Self { rotation, translation };
321 t.validate()?;
322 Ok(t)
323 }
324
325 pub fn translation(t: Point3) -> Self {
326 Self { rotation: Self::identity().rotation, translation: t }
327 }
328
329 pub fn axis_angle(axis: Point3, angle_rad: f64, t: Point3) -> Result<Self, TransformError> {
331 let a = axis.normalized().ok_or(TransformError::NonFinite)?;
332 let (s, c) = angle_rad.sin_cos();
333 let k = 1.0 - c;
334 let rotation = [
335 [c + a.x * a.x * k, a.x * a.y * k - a.z * s, a.x * a.z * k + a.y * s],
336 [a.y * a.x * k + a.z * s, c + a.y * a.y * k, a.y * a.z * k - a.x * s],
337 [a.z * a.x * k - a.y * s, a.z * a.y * k + a.x * s, c + a.z * a.z * k],
338 ];
339 Self::new(rotation, t)
340 }
341
342 pub fn from_frame(origin: Point3, x_axis: Point3, z_axis: Point3) -> Result<Self, TransformError> {
347 let z = z_axis.normalized().ok_or(TransformError::NonFinite)?;
348 let x0 = x_axis.sub(z.scale(x_axis.dot(z)));
349 let x = x0.normalized().ok_or(TransformError::NotOrthonormal)?;
350 let y = z.cross(x);
351 let rotation = [[x.x, x.y, x.z], [y.x, y.y, y.z], [z.x, z.y, z.z]];
354 let rotated_origin = Self { rotation, translation: Point3::ZERO }.apply_vector(origin);
355 Self::new(rotation, rotated_origin.scale(-1.0))
356 }
357
358 pub fn validate(&self) -> Result<(), TransformError> {
359 let r = &self.rotation;
360 if !self.translation.is_finite() || r.iter().flatten().any(|v| !v.is_finite()) {
361 return Err(TransformError::NonFinite);
362 }
363 let row = |i: usize| Point3::new(r[i][0], r[i][1], r[i][2]);
364 let (a, b, c) = (row(0), row(1), row(2));
365 let residual = (a.dot(a) - 1.0).abs().max((b.dot(b) - 1.0).abs()).max((c.dot(c) - 1.0).abs())
366 .max(a.dot(b).abs()).max(a.dot(c).abs()).max(b.dot(c).abs());
367 if residual > Self::ORTHONORMAL_EPS {
368 return Err(TransformError::NotOrthonormal);
369 }
370 if a.cross(b).dot(c) < 0.0 {
371 return Err(TransformError::Mirrored);
372 }
373 Ok(())
374 }
375
376 pub fn is_identity(&self) -> bool {
377 *self == Self::identity()
378 }
379
380 pub fn apply_vector(&self, v: Point3) -> Point3 {
381 let r = &self.rotation;
382 Point3::new(
383 r[0][0] * v.x + r[0][1] * v.y + r[0][2] * v.z,
384 r[1][0] * v.x + r[1][1] * v.y + r[1][2] * v.z,
385 r[2][0] * v.x + r[2][1] * v.y + r[2][2] * v.z,
386 )
387 }
388
389 pub fn apply_point(&self, p: Point3) -> Point3 {
390 self.apply_vector(p).add(self.translation)
391 }
392
393 pub fn apply_bounds(&self, b: &Bounds3) -> Bounds3 {
395 if b.is_empty() {
396 return Bounds3::empty();
397 }
398 let mut out = Bounds3::empty();
399 for i in 0..8 {
400 let p = Point3::new(
401 if i & 1 == 0 { b.min.x } else { b.max.x },
402 if i & 2 == 0 { b.min.y } else { b.max.y },
403 if i & 4 == 0 { b.min.z } else { b.max.z },
404 );
405 out.include(self.apply_point(p));
406 }
407 out
408 }
409
410 pub fn inverse(&self) -> Self {
412 let r = &self.rotation;
413 let rt = [[r[0][0], r[1][0], r[2][0]], [r[0][1], r[1][1], r[2][1]], [r[0][2], r[1][2], r[2][2]]];
414 let back = Self { rotation: rt, translation: Point3::ZERO };
415 let t = back.apply_vector(self.translation).scale(-1.0);
416 Self { rotation: rt, translation: t }
417 }
418
419 pub fn then(&self, next: &RigidTransform) -> Self {
421 let mut rotation = [[0.0; 3]; 3];
422 for (i, row) in rotation.iter_mut().enumerate() {
423 for (j, cell) in row.iter_mut().enumerate() {
424 *cell = (0..3).map(|k| next.rotation[i][k] * self.rotation[k][j]).sum();
425 }
426 }
427 Self { rotation, translation: next.apply_point(self.translation) }
428 }
429}