Skip to main content

semantic_scene/
math.rs

1//! Geometry primitives used by `SemanticScene`.
2
3use std::fmt;
4
5/// Two-dimensional vector.
6#[derive(Debug, Clone, Copy, PartialEq)]
7pub struct Vec2(pub f32, pub f32);
8
9/// Rotation requested while loading a scene descriptor.
10#[derive(Debug, Default, Clone, Copy, PartialEq)]
11pub enum Rotation3 {
12    /// Do not rotate loaded coordinates.
13    #[default]
14    Identity,
15    /// Quaternion represented as `[x, y, z, w]`.
16    ///
17    /// Present to reserve Habitat-Sim's rotation option shape. MP3D loading
18    /// currently rejects this variant with `UnsupportedOption`.
19    Quaternion([f32; 4]),
20}
21
22/// Three-dimensional vector.
23#[derive(Debug, Clone, Copy, PartialEq)]
24pub struct Vec3(pub f32, pub f32, pub f32);
25
26impl Vec3 {
27    /// Returns the cross product of two vectors.
28    #[must_use]
29    pub fn cross(self, other: Self) -> Self {
30        Self(
31            self.1.mul_add(other.2, -(self.2 * other.1)),
32            self.2.mul_add(other.0, -(self.0 * other.2)),
33            self.0.mul_add(other.1, -(self.1 * other.0)),
34        )
35    }
36}
37
38impl fmt::Display for Vec2 {
39    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
40        write!(formatter, "({:.2}, {:.2})", self.0, self.1)
41    }
42}
43
44impl fmt::Display for Vec3 {
45    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
46        write!(formatter, "({:.2}, {:.2}, {:.2})", self.0, self.1, self.2)
47    }
48}
49
50/// Axis-aligned bounding box.
51#[derive(Debug, Clone, Copy, PartialEq)]
52pub struct Aabb {
53    /// Minimum corner of the box.
54    pub min: Vec3,
55    /// Maximum corner of the box.
56    pub max: Vec3,
57}
58
59impl Aabb {
60    /// Returns whether the point lies inside or on the box boundary.
61    #[must_use]
62    pub fn contains(self, point: Vec3) -> bool {
63        point.0 >= self.min.0
64            && point.0 <= self.max.0
65            && point.1 >= self.min.1
66            && point.1 <= self.max.1
67            && point.2 >= self.min.2
68            && point.2 <= self.max.2
69    }
70}
71
72impl fmt::Display for Aabb {
73    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
74        write!(formatter, "min={}, max={}", self.min, self.max)
75    }
76}
77
78/// Oriented bounding box.
79#[derive(Debug, Clone, Copy, PartialEq)]
80pub struct Obb {
81    /// Box center.
82    pub center: Vec3,
83    /// First local box axis.
84    pub axis0: Vec3,
85    /// Second local box axis.
86    pub axis1: Vec3,
87    /// Third local box axis.
88    pub axis2: Vec3,
89    /// Half of the box dimensions along each local axis.
90    pub half_extents: Vec3,
91}
92
93impl Obb {
94    /// Returns the axis-aligned bounding box enclosing this oriented box.
95    #[must_use]
96    pub fn to_aabb(self) -> Aabb {
97        let ex = extent(self.axis0.0, self.axis1.0, self.axis2.0, self.half_extents);
98        let ey = extent(self.axis0.1, self.axis1.1, self.axis2.1, self.half_extents);
99        let ez = extent(self.axis0.2, self.axis1.2, self.axis2.2, self.half_extents);
100
101        Aabb {
102            min: Vec3(self.center.0 - ex, self.center.1 - ey, self.center.2 - ez),
103            max: Vec3(self.center.0 + ex, self.center.1 + ey, self.center.2 + ez),
104        }
105    }
106}
107
108impl fmt::Display for Obb {
109    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
110        write!(
111            formatter,
112            "center={}, half_extents={}",
113            self.center, self.half_extents
114        )
115    }
116}
117
118fn extent(axis0: f32, axis1: f32, axis2: f32, half_extents: Vec3) -> f32 {
119    axis2.abs().mul_add(
120        half_extents.2,
121        axis0
122            .abs()
123            .mul_add(half_extents.0, axis1.abs() * half_extents.1),
124    )
125}