Skip to main content

semantic_scene/scene/
region.rs

1//! Semantic region model.
2
3use std::fmt;
4
5use crate::{Aabb, Vec2, Vec3};
6
7use super::SemanticObject;
8
9/// Represents a semantic region, typically a room in a level of a house.
10///
11/// MP3D descriptors provide an AABB but not polyloop geometry. Polyloop-related
12/// accessors return empty slices unless a dataset supplies that geometry.
13#[derive(Debug, Clone, PartialEq)]
14pub struct SemanticRegion<ObjectCategory, RegionCategory> {
15    index: i32,
16    category: RegionCategory,
17    position: Vec3,
18    aabb: Aabb,
19    objects: Vec<SemanticObject<ObjectCategory>>,
20    poly_loop_points: Vec<Vec2>,
21    volume_edges: Vec<Vec<Vec3>>,
22    floor_height: f64,
23    extrusion_height: f64,
24    area: f64,
25}
26
27impl<ObjectCategory, RegionCategory> SemanticRegion<ObjectCategory, RegionCategory> {
28    /// Creates a semantic region.
29    ///
30    /// `index` is the original descriptor index for this region.
31    #[must_use]
32    pub fn new(
33        index: i32,
34        category: RegionCategory,
35        position: Vec3,
36        aabb: Aabb,
37        objects: Vec<SemanticObject<ObjectCategory>>,
38    ) -> Self {
39        Self {
40            index,
41            category,
42            position,
43            aabb,
44            objects,
45            poly_loop_points: Vec::new(),
46            volume_edges: Vec::new(),
47            floor_height: f64::from(aabb.min.1),
48            extrusion_height: f64::from(aabb.max.1 - aabb.min.1),
49            area: f64::from((aabb.max.0 - aabb.min.0).abs() * (aabb.max.2 - aabb.min.2).abs()),
50        }
51    }
52
53    /// Returns the source descriptor index.
54    #[must_use]
55    pub const fn index(&self) -> i32 {
56        self.index
57    }
58
59    /// Returns the semantic category of the region.
60    #[must_use]
61    pub const fn category(&self) -> &RegionCategory {
62        &self.category
63    }
64
65    /// Returns the region position from the source descriptor.
66    #[must_use]
67    pub const fn position(&self) -> Vec3 {
68        self.position
69    }
70
71    /// Returns the region axis-aligned bounding box.
72    #[must_use]
73    pub const fn aabb(&self) -> &Aabb {
74        &self.aabb
75    }
76
77    /// Returns objects contained by this region.
78    #[must_use]
79    pub fn objects(&self) -> &[SemanticObject<ObjectCategory>] {
80        &self.objects
81    }
82
83    /// Tests whether this region contains the point.
84    ///
85    /// For MP3D-loaded scenes this uses the region AABB. Future descriptor
86    /// loaders with polyloop geometry can provide tighter containment.
87    #[must_use]
88    pub fn contains(&self, point: Vec3) -> bool {
89        self.aabb.contains(point)
90    }
91
92    /// Returns points making up the region's floor-parallel polyloop.
93    #[must_use]
94    pub fn poly_loop_points(&self) -> &[Vec2] {
95        &self.poly_loop_points
96    }
97
98    /// Returns bounding volume edges for visualization.
99    #[must_use]
100    pub fn volume_edges(&self) -> &[Vec<Vec3>] {
101        &self.volume_edges
102    }
103
104    /// Returns the floor height above the x-z plane.
105    #[must_use]
106    pub const fn floor_height(&self) -> f64 {
107        self.floor_height
108    }
109
110    /// Returns the extrusion height above the floor.
111    #[must_use]
112    pub const fn extrusion_height(&self) -> f64 {
113        self.extrusion_height
114    }
115
116    /// Returns the area of the region base.
117    #[must_use]
118    pub const fn area(&self) -> f64 {
119        self.area
120    }
121
122    /// Returns the volume of the region bounds.
123    #[must_use]
124    pub const fn volume(&self) -> f64 {
125        self.area * self.extrusion_height
126    }
127}
128
129impl<OC, RC: fmt::Display> fmt::Display for SemanticRegion<OC, RC> {
130    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
131        write!(
132            formatter,
133            "region {}: category={}, objects={}, bounds=[{}]",
134            self.index,
135            self.category,
136            self.objects.len(),
137            self.aabb
138        )
139    }
140}