use super::super::GeometryRouter;
use super::mat4_to_col_array;
use crate::Result;
use ifc_lite_core::{DecodedEntity, EntityDecoder, IfcType};
use nalgebra::Matrix4;
#[derive(Clone, Copy)]
pub(super) struct PlacementWalk {
pub(super) transform: Matrix4<f64>,
pub(super) truncated: bool,
}
impl PlacementWalk {
pub(super) fn complete(transform: Matrix4<f64>) -> Self {
Self { transform, truncated: false }
}
}
impl GeometryRouter {
pub(super) fn get_placement_transform(
&self,
placement: &DecodedEntity,
decoder: &mut EntityDecoder,
) -> Result<Matrix4<f64>> {
Ok(self
.get_placement_transform_with_depth(placement, decoder, 0)?
.transform)
}
pub(super) const MAX_PLACEMENT_DEPTH: usize = ifc_lite_core::MAX_PLACEMENT_DEPTH;
pub(super) fn get_placement_transform_with_depth(
&self,
placement: &DecodedEntity,
decoder: &mut EntityDecoder,
depth: usize,
) -> Result<PlacementWalk> {
if let Some(m) = decoder.get_placement_transform_cached(placement.id) {
return Ok(PlacementWalk::complete(Matrix4::from_column_slice(&m)));
}
if depth > Self::MAX_PLACEMENT_DEPTH {
return Ok(PlacementWalk { transform: Matrix4::identity(), truncated: true });
}
if placement.ifc_type == IfcType::IfcLinearPlacement {
let walk = self.resolve_linear_placement_with_depth(placement, decoder, depth)?;
if !walk.truncated {
decoder
.cache_placement_transform(placement.id, mat4_to_col_array(&walk.transform));
}
return Ok(walk);
}
if placement.ifc_type == IfcType::IfcGridPlacement {
let walk = self.resolve_grid_placement_with_depth(placement, decoder, depth)?;
if !walk.truncated {
decoder
.cache_placement_transform(placement.id, mat4_to_col_array(&walk.transform));
}
return Ok(walk);
}
if placement.ifc_type != IfcType::IfcLocalPlacement {
return Ok(PlacementWalk::complete(Matrix4::identity()));
}
let parent = match placement.get(0) {
Some(parent_attr) if !parent_attr.is_null() => {
match decoder.resolve_ref(parent_attr)? {
Some(p) => self.get_placement_transform_with_depth(&p, decoder, depth + 1)?,
None => PlacementWalk::complete(Matrix4::identity()),
}
}
_ => PlacementWalk::complete(Matrix4::identity()),
};
let local_transform = if let Some(rel_attr) = placement.get(1) {
if !rel_attr.is_null() {
if let Some(rel) = decoder.resolve_ref(rel_attr)? {
if rel.ifc_type == IfcType::IfcAxis2Placement3D {
self.parse_axis2_placement_3d(&rel, decoder)?
} else {
Matrix4::identity()
}
} else {
Matrix4::identity()
}
} else {
Matrix4::identity()
}
} else {
Matrix4::identity()
};
let result = parent.transform * local_transform;
if !parent.truncated {
decoder.cache_placement_transform(placement.id, mat4_to_col_array(&result));
}
Ok(PlacementWalk { transform: result, truncated: parent.truncated })
}
}