use crate::{EntityId, IfcType};
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum SpatialNodeType {
Project,
Site,
Building,
Storey,
Space,
Element,
Facility,
FacilityPart,
}
impl SpatialNodeType {
pub fn display_name(&self) -> &'static str {
match self {
SpatialNodeType::Project => "Project",
SpatialNodeType::Site => "Site",
SpatialNodeType::Building => "Building",
SpatialNodeType::Storey => "Storey",
SpatialNodeType::Space => "Space",
SpatialNodeType::Element => "Element",
SpatialNodeType::Facility => "Facility",
SpatialNodeType::FacilityPart => "Facility Part",
}
}
pub fn icon(&self) -> &'static str {
match self {
SpatialNodeType::Project => "📋",
SpatialNodeType::Site => "🌍",
SpatialNodeType::Building => "🏢",
SpatialNodeType::Storey => "📐",
SpatialNodeType::Space => "🚪",
SpatialNodeType::Element => "🧱",
SpatialNodeType::Facility => "🛣️",
SpatialNodeType::FacilityPart => "🔧",
}
}
pub fn from_ifc_type(ifc_type: &IfcType) -> Self {
match ifc_type {
IfcType::IfcProject => SpatialNodeType::Project,
IfcType::IfcSite => SpatialNodeType::Site,
IfcType::IfcBuilding => SpatialNodeType::Building,
IfcType::IfcBuildingStorey => SpatialNodeType::Storey,
IfcType::IfcSpace => SpatialNodeType::Space,
IfcType::IfcFacility | IfcType::IfcRoad | IfcType::IfcBridge | IfcType::IfcRailway => {
SpatialNodeType::Facility
}
IfcType::IfcFacilityPart
| IfcType::IfcRoadPart
| IfcType::IfcBridgePart
| IfcType::IfcRailwayPart => SpatialNodeType::FacilityPart,
_ => SpatialNodeType::Element,
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SpatialNode {
pub id: EntityId,
pub node_type: SpatialNodeType,
pub name: String,
pub entity_type: String,
pub elevation: Option<f32>,
pub children: Vec<SpatialNode>,
pub has_geometry: bool,
}
impl SpatialNode {
pub fn new(
id: EntityId,
node_type: SpatialNodeType,
name: impl Into<String>,
entity_type: impl Into<String>,
) -> Self {
Self {
id,
node_type,
name: name.into(),
entity_type: entity_type.into(),
elevation: None,
children: Vec::new(),
has_geometry: false,
}
}
pub fn with_elevation(mut self, elevation: f32) -> Self {
self.elevation = Some(elevation);
self
}
pub fn with_geometry(mut self, has_geometry: bool) -> Self {
self.has_geometry = has_geometry;
self
}
pub fn add_child(&mut self, child: SpatialNode) {
self.children.push(child);
}
pub fn element_count(&self) -> usize {
let own = if self.node_type == SpatialNodeType::Element {
1
} else {
0
};
own + self
.children
.iter()
.map(|c| c.element_count())
.sum::<usize>()
}
pub fn find(&self, id: EntityId) -> Option<&SpatialNode> {
if self.id == id {
return Some(self);
}
for child in &self.children {
if let Some(found) = child.find(id) {
return Some(found);
}
}
None
}
pub fn find_mut(&mut self, id: EntityId) -> Option<&mut SpatialNode> {
if self.id == id {
return Some(self);
}
for child in &mut self.children {
if let Some(found) = child.find_mut(id) {
return Some(found);
}
}
None
}
pub fn iter(&self) -> SpatialNodeIter<'_> {
SpatialNodeIter { stack: vec![self] }
}
pub fn element_ids(&self) -> Vec<EntityId> {
self.iter()
.filter(|n| n.node_type == SpatialNodeType::Element)
.map(|n| n.id)
.collect()
}
}
pub struct SpatialNodeIter<'a> {
stack: Vec<&'a SpatialNode>,
}
impl<'a> Iterator for SpatialNodeIter<'a> {
type Item = &'a SpatialNode;
fn next(&mut self) -> Option<Self::Item> {
let node = self.stack.pop()?;
for child in node.children.iter().rev() {
self.stack.push(child);
}
Some(node)
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct StoreyInfo {
pub id: EntityId,
pub name: String,
pub elevation: f32,
pub element_count: usize,
}
impl StoreyInfo {
pub fn new(
id: EntityId,
name: impl Into<String>,
elevation: f32,
element_count: usize,
) -> Self {
Self {
id,
name: name.into(),
elevation,
element_count,
}
}
}
pub trait SpatialQuery: Send + Sync {
fn spatial_tree(&self) -> Option<&SpatialNode>;
fn storeys(&self) -> Vec<StoreyInfo>;
fn elements_in_storey(&self, storey_id: EntityId) -> Vec<EntityId>;
fn containing_storey(&self, element_id: EntityId) -> Option<EntityId>;
fn search(&self, query: &str) -> Vec<EntityId>;
fn elements_by_type(&self, ifc_type: &IfcType) -> Vec<EntityId>;
fn all_elements(&self) -> Vec<EntityId> {
if let Some(tree) = self.spatial_tree() {
tree.element_ids()
} else {
Vec::new()
}
}
fn element_count(&self) -> usize {
self.all_elements().len()
}
}