semantic-scene 0.1.1

Rust parser for semantic scene descriptors, currently focused on Habitat-Sim Matterport3D .house files.
Documentation
//! Semantic category types and mapping selectors.

use std::fmt;

/// `Matterport3D` category mapping selector.
///
/// MP3D object categories can be read from the dataset's raw mapping or the
/// `mpcat40` mapping. Region categories use the room category map.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Mp3dCategoryMapping {
    /// Dataset-specific default mapping.
    Default,
    /// Raw dataset category mapping.
    Raw,
    /// `Matterport3D` `mpcat40` mapping.
    MpCat40,
    /// MP3D room category mapping.
    Category,
}

/// `Matterport3D` semantic object category.
///
/// The default mapping is `mpcat40`, and `Raw` returns the original MP3D
/// dataset category mapping.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Mp3dObjectCategory {
    source_index: i32,
    raw_index: i32,
    raw_name: String,
    mpcat40_index: i32,
    mpcat40_name: String,
}

impl Mp3dObjectCategory {
    #[must_use]
    pub(crate) const fn new(
        source_index: i32,
        raw_index: i32,
        raw_name: String,
        mpcat40_index: i32,
        mpcat40_name: String,
    ) -> Self {
        Self {
            source_index,
            raw_index,
            raw_name,
            mpcat40_index,
            mpcat40_name,
        }
    }

    /// Returns the category index from the source descriptor.
    #[must_use]
    pub const fn source_index(&self) -> i32 {
        self.source_index
    }

    /// Returns the category index under the requested mapping.
    #[must_use]
    pub const fn index(&self, mapping: Mp3dCategoryMapping) -> Option<i32> {
        match mapping {
            Mp3dCategoryMapping::Default | Mp3dCategoryMapping::MpCat40 => Some(self.mpcat40_index),
            Mp3dCategoryMapping::Raw => Some(self.raw_index),
            Mp3dCategoryMapping::Category => None,
        }
    }

    /// Returns the category name under the requested mapping.
    #[must_use]
    pub fn name(&self, mapping: Mp3dCategoryMapping) -> Option<&str> {
        match mapping {
            Mp3dCategoryMapping::Default | Mp3dCategoryMapping::MpCat40 => Some(&self.mpcat40_name),
            Mp3dCategoryMapping::Raw => Some(&self.raw_name),
            Mp3dCategoryMapping::Category => None,
        }
    }
}

impl fmt::Display for Mp3dObjectCategory {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            formatter,
            "mpcat40={}({}), raw={}({})",
            self.mpcat40_name, self.mpcat40_index, self.raw_name, self.raw_index
        )
    }
}

/// `Matterport3D` semantic region category.
///
/// MP3D region categories are encoded as a single character in each `R` record
/// and resolved through the MP3D room category map.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Mp3dRegionCategory {
    code: char,
}

impl Mp3dRegionCategory {
    #[must_use]
    pub(crate) const fn new(code: char) -> Self {
        Self { code }
    }

    /// Returns the MP3D region category code.
    #[must_use]
    pub const fn code(self) -> char {
        self.code
    }

    /// Returns this region category's ordinal in the MP3D room category map.
    #[must_use]
    pub fn index(self) -> Option<i32> {
        region_category_entries()
            .iter()
            .position(|(code, _)| *code == self.code)
            .and_then(|index| i32::try_from(index).ok())
    }

    /// Returns this region category's MP3D room category name.
    #[must_use]
    pub fn name(self) -> Option<&'static str> {
        region_category_entries()
            .iter()
            .find_map(|(code, name)| (*code == self.code).then_some(*name))
    }
}

impl fmt::Display for Mp3dRegionCategory {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        let name = self.name().unwrap_or("unknown");
        write!(formatter, "{name}({})", self.code)
    }
}

const fn region_category_entries() -> &'static [(char, &'static str)] {
    &[
        ('a', "bathroom"),
        ('b', "bedroom"),
        ('c', "closet"),
        ('d', "dining room"),
        ('e', "entryway/foyer/lobby"),
        ('f', "familyroom/lounge"),
        ('g', "garage"),
        ('h', "hallway"),
        ('i', "library"),
        ('j', "laundryroom/mudroom"),
        ('k', "kitchen"),
        ('l', "living room"),
        ('m', "meetingroom/conferenceroom"),
        ('n', "lounge"),
        ('o', "office"),
        ('p', "porch/terrace/deck"),
        ('r', "rec/game"),
        ('s', "stairs"),
        ('t', "toilet"),
        ('u', "utilityroom/toolroom"),
        ('v', "tv"),
        ('w', "workout/gym/exercise"),
        ('x', "outdoor"),
        ('y', "balcony"),
        ('z', "other room"),
        ('B', "bar"),
        ('C', "classroom"),
        ('D', "dining booth"),
        ('S', "spa/sauna"),
        ('Z', "junk"),
    ]
}