Skip to main content

semantic_scene/mp3d/
category.rs

1//! Semantic category types and mapping selectors.
2
3use std::fmt;
4
5/// `Matterport3D` category mapping selector.
6///
7/// MP3D object categories can be read from the dataset's raw mapping or the
8/// `mpcat40` mapping. Region categories use the room category map.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum Mp3dCategoryMapping {
11    /// Dataset-specific default mapping.
12    Default,
13    /// Raw dataset category mapping.
14    Raw,
15    /// `Matterport3D` `mpcat40` mapping.
16    MpCat40,
17    /// MP3D room category mapping.
18    Category,
19}
20
21/// `Matterport3D` semantic object category.
22///
23/// The default mapping is `mpcat40`, and `Raw` returns the original MP3D
24/// dataset category mapping.
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct Mp3dObjectCategory {
27    source_index: i32,
28    raw_index: i32,
29    raw_name: String,
30    mpcat40_index: i32,
31    mpcat40_name: String,
32}
33
34impl Mp3dObjectCategory {
35    #[must_use]
36    pub(crate) const fn new(
37        source_index: i32,
38        raw_index: i32,
39        raw_name: String,
40        mpcat40_index: i32,
41        mpcat40_name: String,
42    ) -> Self {
43        Self {
44            source_index,
45            raw_index,
46            raw_name,
47            mpcat40_index,
48            mpcat40_name,
49        }
50    }
51
52    /// Returns the category index from the source descriptor.
53    #[must_use]
54    pub const fn source_index(&self) -> i32 {
55        self.source_index
56    }
57
58    /// Returns the category index under the requested mapping.
59    #[must_use]
60    pub const fn index(&self, mapping: Mp3dCategoryMapping) -> Option<i32> {
61        match mapping {
62            Mp3dCategoryMapping::Default | Mp3dCategoryMapping::MpCat40 => Some(self.mpcat40_index),
63            Mp3dCategoryMapping::Raw => Some(self.raw_index),
64            Mp3dCategoryMapping::Category => None,
65        }
66    }
67
68    /// Returns the category name under the requested mapping.
69    #[must_use]
70    pub fn name(&self, mapping: Mp3dCategoryMapping) -> Option<&str> {
71        match mapping {
72            Mp3dCategoryMapping::Default | Mp3dCategoryMapping::MpCat40 => Some(&self.mpcat40_name),
73            Mp3dCategoryMapping::Raw => Some(&self.raw_name),
74            Mp3dCategoryMapping::Category => None,
75        }
76    }
77}
78
79impl fmt::Display for Mp3dObjectCategory {
80    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
81        write!(
82            formatter,
83            "mpcat40={}({}), raw={}({})",
84            self.mpcat40_name, self.mpcat40_index, self.raw_name, self.raw_index
85        )
86    }
87}
88
89/// `Matterport3D` semantic region category.
90///
91/// MP3D region categories are encoded as a single character in each `R` record
92/// and resolved through the MP3D room category map.
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub struct Mp3dRegionCategory {
95    code: char,
96}
97
98impl Mp3dRegionCategory {
99    #[must_use]
100    pub(crate) const fn new(code: char) -> Self {
101        Self { code }
102    }
103
104    /// Returns the MP3D region category code.
105    #[must_use]
106    pub const fn code(self) -> char {
107        self.code
108    }
109
110    /// Returns this region category's ordinal in the MP3D room category map.
111    #[must_use]
112    pub fn index(self) -> Option<i32> {
113        region_category_entries()
114            .iter()
115            .position(|(code, _)| *code == self.code)
116            .and_then(|index| i32::try_from(index).ok())
117    }
118
119    /// Returns this region category's MP3D room category name.
120    #[must_use]
121    pub fn name(self) -> Option<&'static str> {
122        region_category_entries()
123            .iter()
124            .find_map(|(code, name)| (*code == self.code).then_some(*name))
125    }
126}
127
128impl fmt::Display for Mp3dRegionCategory {
129    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
130        let name = self.name().unwrap_or("unknown");
131        write!(formatter, "{name}({})", self.code)
132    }
133}
134
135const fn region_category_entries() -> &'static [(char, &'static str)] {
136    &[
137        ('a', "bathroom"),
138        ('b', "bedroom"),
139        ('c', "closet"),
140        ('d', "dining room"),
141        ('e', "entryway/foyer/lobby"),
142        ('f', "familyroom/lounge"),
143        ('g', "garage"),
144        ('h', "hallway"),
145        ('i', "library"),
146        ('j', "laundryroom/mudroom"),
147        ('k', "kitchen"),
148        ('l', "living room"),
149        ('m', "meetingroom/conferenceroom"),
150        ('n', "lounge"),
151        ('o', "office"),
152        ('p', "porch/terrace/deck"),
153        ('r', "rec/game"),
154        ('s', "stairs"),
155        ('t', "toilet"),
156        ('u', "utilityroom/toolroom"),
157        ('v', "tv"),
158        ('w', "workout/gym/exercise"),
159        ('x', "outdoor"),
160        ('y', "balcony"),
161        ('z', "other room"),
162        ('B', "bar"),
163        ('C', "classroom"),
164        ('D', "dining booth"),
165        ('S', "spa/sauna"),
166        ('Z', "junk"),
167    ]
168}