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