Skip to main content

semantic_scene/scene/
mod.rs

1//! Semantic scene graph model.
2
3use std::fmt;
4
5mod category;
6mod level;
7mod object;
8mod region;
9
10use std::{collections::HashMap, path::Path};
11
12pub use category::{Category, CategoryMapping, RegionCategory};
13pub use level::SemanticLevel;
14pub use object::SemanticObject;
15pub use region::SemanticRegion;
16
17use crate::{Aabb, LoadError, Mp3dLoader, Mp3dOptions, SemanticSceneLoader};
18
19/// Scene element kind used by `SemanticScene::count`.
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21pub enum ElementKind {
22    /// Image count from the descriptor header.
23    Images,
24    /// Panorama count from the descriptor header.
25    Panoramas,
26    /// Vertex count from the descriptor header.
27    Vertices,
28    /// Surface count from the descriptor header.
29    Surfaces,
30    /// Segment count from the descriptor header.
31    Segments,
32    /// Object count from the descriptor header.
33    Objects,
34    /// Category count from the descriptor header.
35    Categories,
36    /// Region count from the descriptor header.
37    Regions,
38    /// Portal count from the descriptor header.
39    Portals,
40    /// Level count from the descriptor header.
41    Levels,
42}
43
44impl ElementKind {
45    pub(crate) const fn as_str(self) -> &'static str {
46        match self {
47            Self::Images => "images",
48            Self::Panoramas => "panoramas",
49            Self::Vertices => "vertices",
50            Self::Surfaces => "surfaces",
51            Self::Segments => "segments",
52            Self::Objects => "objects",
53            Self::Categories => "categories",
54            Self::Regions => "regions",
55            Self::Portals => "portals",
56            Self::Levels => "levels",
57        }
58    }
59}
60
61/// Scene containing semantically annotated levels, regions, and objects.
62///
63/// This mirrors Habitat-Sim's `SemanticScene` at the API-shape level while
64/// using Rust ownership and indices instead of shared-pointer graphs.
65#[derive(Debug, Default, Clone)]
66pub struct SemanticScene {
67    pub(crate) name: String,
68    pub(crate) label: String,
69    pub(crate) counts: HashMap<&'static str, usize>,
70    pub(crate) aabb: Option<Aabb>,
71    pub(crate) categories: Vec<Category>,
72    pub(crate) levels: Vec<SemanticLevel>,
73    pub(crate) regions: Vec<SemanticRegion>,
74    pub(crate) objects: Vec<SemanticObject>,
75    pub(crate) semantic_index_map: HashMap<i32, usize>,
76}
77
78impl SemanticScene {
79    pub(crate) fn new() -> Self {
80        Self::default()
81    }
82
83    /// Returns the scene name from the descriptor header.
84    #[must_use]
85    pub fn name(&self) -> &str {
86        &self.name
87    }
88
89    /// Returns the scene label from the descriptor header.
90    #[must_use]
91    pub fn label(&self) -> &str {
92        &self.label
93    }
94
95    /// Returns the scene axis-aligned bounding box.
96    ///
97    /// Habitat-Sim exposes this as `aabb()`. It is optional here because a
98    /// malformed or partially built scene may not have a parsed header.
99    #[must_use]
100    pub const fn aabb(&self) -> Option<&Aabb> {
101        self.aabb.as_ref()
102    }
103
104    /// Returns the total number of a given element type from the descriptor header.
105    #[must_use]
106    pub fn count(&self, element: ElementKind) -> Option<usize> {
107        self.counts.get(element.as_str()).copied()
108    }
109
110    /// Returns all semantic object categories in the scene.
111    #[must_use]
112    pub fn categories(&self) -> &[Category] {
113        &self.categories
114    }
115
116    /// Returns all levels in the scene.
117    #[must_use]
118    pub fn levels(&self) -> &[SemanticLevel] {
119        &self.levels
120    }
121
122    /// Returns all regions in the scene.
123    #[must_use]
124    pub fn regions(&self) -> &[SemanticRegion] {
125        &self.regions
126    }
127
128    /// Returns all semantic objects in the scene.
129    #[must_use]
130    pub fn objects(&self) -> &[SemanticObject] {
131        &self.objects
132    }
133
134    /// Returns the semantic mesh mask index to object index map.
135    #[must_use]
136    pub const fn semantic_index_map(&self) -> &HashMap<i32, usize> {
137        &self.semantic_index_map
138    }
139
140    /// Converts a semantic mesh mask index to an object index.
141    ///
142    /// Habitat-Sim returns `ID_UNDEFINED` on miss. This Rust API returns
143    /// `None` instead.
144    #[must_use]
145    pub fn semantic_index_to_object_index(&self, mask_index: i32) -> Option<usize> {
146        self.semantic_index_map.get(&mask_index).copied()
147    }
148
149    /// Loads a `Matterport3D` semantic scene from a `.house` file.
150    ///
151    /// # Errors
152    ///
153    /// Returns an error if the file cannot be read or the `.house` descriptor is invalid.
154    pub fn load_mp3d_house(path: impl AsRef<Path>) -> Result<Self, LoadError> {
155        Mp3dLoader::from_path(path, Mp3dOptions::default())
156    }
157
158    pub(crate) fn set_header(
159        &mut self,
160        name: String,
161        label: String,
162        counts: HashMap<&'static str, usize>,
163        aabb: Aabb,
164    ) {
165        self.name = name;
166        self.label = label;
167        self.counts = counts;
168        self.aabb = Some(aabb);
169    }
170
171    pub(crate) fn push_category(&mut self, category: Category) {
172        self.categories.push(category);
173    }
174
175    pub(crate) fn push_level(&mut self, level: SemanticLevel) {
176        self.levels.push(level);
177    }
178
179    pub(crate) fn push_region(&mut self, region: SemanticRegion) {
180        self.regions.push(region);
181    }
182
183    pub(crate) fn push_object(&mut self, object: SemanticObject) {
184        self.objects.push(object);
185    }
186
187    pub(crate) fn insert_segment(&mut self, segment_id: i32, object_index: usize) -> Option<usize> {
188        self.semantic_index_map.insert(segment_id, object_index)
189    }
190
191    pub(crate) fn level_position_by_index(&self, index: i32) -> Option<usize> {
192        self.levels.iter().position(|level| level.index() == index)
193    }
194
195    pub(crate) fn region_position_by_index(&self, index: i32) -> Option<usize> {
196        self.regions
197            .iter()
198            .position(|region| region.index() == index)
199    }
200
201    pub(crate) fn category_position_by_index(&self, index: i32) -> Option<usize> {
202        self.categories
203            .iter()
204            .position(|category| category.source_index() == index)
205    }
206
207    pub(crate) fn object_position_by_index(&self, index: i32) -> Option<usize> {
208        self.objects
209            .iter()
210            .position(|object| object.index() == index)
211    }
212
213    pub(crate) fn level_mut(&mut self, index: usize) -> Option<&mut SemanticLevel> {
214        self.levels.get_mut(index)
215    }
216
217    pub(crate) fn region_mut(&mut self, index: usize) -> Option<&mut SemanticRegion> {
218        self.regions.get_mut(index)
219    }
220}
221
222impl fmt::Display for SemanticScene {
223    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
224        if formatter.alternate() {
225            write!(
226                formatter,
227                "SemanticScene {:?}\n  label: {:?}\n  bounds: {}\n  counts: {}",
228                self.name,
229                self.label,
230                self.display_aabb(),
231                self.display_counts()
232            )
233        } else {
234            write!(
235                formatter,
236                "SemanticScene {:?}: {}, bounds=[{}]",
237                self.name,
238                self.display_counts(),
239                self.display_aabb()
240            )
241        }
242    }
243}
244
245impl SemanticScene {
246    const fn display_aabb(&self) -> DisplayAabb<'_> {
247        DisplayAabb(self.aabb.as_ref())
248    }
249
250    const fn display_counts(&self) -> DisplayCounts<'_> {
251        DisplayCounts(self)
252    }
253}
254
255struct DisplayAabb<'a>(Option<&'a Aabb>);
256
257impl fmt::Display for DisplayAabb<'_> {
258    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
259        match self.0 {
260            Some(aabb) => aabb.fmt(formatter),
261            None => formatter.write_str("none"),
262        }
263    }
264}
265
266struct DisplayCounts<'a>(&'a SemanticScene);
267
268impl fmt::Display for DisplayCounts<'_> {
269    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
270        write!(
271            formatter,
272            "levels={}, regions={}, objects={}, categories={}, segments={}",
273            self.0
274                .count(ElementKind::Levels)
275                .unwrap_or(self.0.levels.len()),
276            self.0
277                .count(ElementKind::Regions)
278                .unwrap_or(self.0.regions.len()),
279            self.0
280                .count(ElementKind::Objects)
281                .unwrap_or(self.0.objects.len()),
282            self.0
283                .count(ElementKind::Categories)
284                .unwrap_or(self.0.categories.len()),
285            self.0
286                .count(ElementKind::Segments)
287                .unwrap_or(self.0.semantic_index_map.len()),
288        )
289    }
290}