use super::GeometryRouter;
use crate::{Error, Mesh, Point3, Result, Vector3};
use ifc_lite_core::{DecodedEntity, EntityDecoder, IfcType};
use nalgebra::Matrix4;
impl GeometryRouter {
pub(super) fn apply_placement(
&self,
element: &DecodedEntity,
decoder: &mut EntityDecoder,
mesh: &mut Mesh,
) -> Result<()> {
let placement_attr = match element.get(5) {
Some(attr) if !attr.is_null() => attr,
_ => return Ok(()),
};
let placement = match decoder.resolve_ref(placement_attr)? {
Some(p) => p,
None => return Ok(()),
};
let mut transform = self.get_placement_transform(&placement, decoder)?;
self.scale_transform(&mut transform);
self.transform_mesh(mesh, &transform);
Ok(())
}
pub(super) fn get_placement_transform_from_element(
&self,
element: &DecodedEntity,
decoder: &mut EntityDecoder,
) -> Result<Matrix4<f64>> {
let placement_attr = match element.get(5) {
Some(attr) if !attr.is_null() => attr,
_ => return Ok(Matrix4::identity()), };
let placement = match decoder.resolve_ref(placement_attr)? {
Some(p) => p,
None => return Ok(Matrix4::identity()),
};
self.get_placement_transform(&placement, decoder)
}
pub(super) fn get_placement_transform(
&self,
placement: &DecodedEntity,
decoder: &mut EntityDecoder,
) -> Result<Matrix4<f64>> {
self.get_placement_transform_with_depth(placement, decoder, 0)
}
const MAX_PLACEMENT_DEPTH: usize = 32;
fn get_placement_transform_with_depth(
&self,
placement: &DecodedEntity,
decoder: &mut EntityDecoder,
depth: usize,
) -> Result<Matrix4<f64>> {
if depth > Self::MAX_PLACEMENT_DEPTH {
return Ok(Matrix4::identity());
}
if placement.ifc_type != IfcType::IfcLocalPlacement {
return Ok(Matrix4::identity());
}
let parent_transform = if let Some(parent_attr) = placement.get(0) {
if !parent_attr.is_null() {
if let Some(parent) = decoder.resolve_ref(parent_attr)? {
self.get_placement_transform_with_depth(&parent, decoder, depth + 1)?
} else {
Matrix4::identity()
}
} else {
Matrix4::identity()
}
} else {
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()
};
Ok(parent_transform * local_transform)
}
pub(super) fn parse_axis2_placement_3d(
&self,
placement: &DecodedEntity,
decoder: &mut EntityDecoder,
) -> Result<Matrix4<f64>> {
let location = self.parse_cartesian_point(placement, decoder, 0)?;
let z_axis = if let Some(axis_attr) = placement.get(1) {
if !axis_attr.is_null() {
if let Some(axis_entity) = decoder.resolve_ref(axis_attr)? {
self.parse_direction(&axis_entity)?
} else {
Vector3::new(0.0, 0.0, 1.0)
}
} else {
Vector3::new(0.0, 0.0, 1.0)
}
} else {
Vector3::new(0.0, 0.0, 1.0)
};
let x_axis = if let Some(ref_dir_attr) = placement.get(2) {
if !ref_dir_attr.is_null() {
if let Some(ref_dir_entity) = decoder.resolve_ref(ref_dir_attr)? {
self.parse_direction(&ref_dir_entity)?
} else {
Vector3::new(1.0, 0.0, 0.0)
}
} else {
Vector3::new(1.0, 0.0, 0.0)
}
} else {
Vector3::new(1.0, 0.0, 0.0)
};
let y_axis = z_axis.cross(&x_axis).normalize();
let x_axis = y_axis.cross(&z_axis).normalize();
let z_axis = z_axis.normalize();
let mut transform = Matrix4::identity();
transform[(0, 0)] = x_axis.x;
transform[(1, 0)] = x_axis.y;
transform[(2, 0)] = x_axis.z;
transform[(0, 1)] = y_axis.x;
transform[(1, 1)] = y_axis.y;
transform[(2, 1)] = y_axis.z;
transform[(0, 2)] = z_axis.x;
transform[(1, 2)] = z_axis.y;
transform[(2, 2)] = z_axis.z;
transform[(0, 3)] = location.x;
transform[(1, 3)] = location.y;
transform[(2, 3)] = location.z;
Ok(transform)
}
#[inline]
pub(super) fn parse_cartesian_point(
&self,
parent: &DecodedEntity,
decoder: &mut EntityDecoder,
attr_index: usize,
) -> Result<Point3<f64>> {
let point_attr = parent
.get(attr_index)
.ok_or_else(|| Error::geometry("Missing cartesian point".to_string()))?;
let point_entity = decoder
.resolve_ref(point_attr)?
.ok_or_else(|| Error::geometry("Failed to resolve cartesian point".to_string()))?;
if point_entity.ifc_type != IfcType::IfcCartesianPoint {
return Err(Error::geometry(format!(
"Expected IfcCartesianPoint, got {}",
point_entity.ifc_type
)));
}
let coords_attr = point_entity
.get(0)
.ok_or_else(|| Error::geometry("IfcCartesianPoint missing coordinates".to_string()))?;
let coords = coords_attr
.as_list()
.ok_or_else(|| Error::geometry("Expected coordinate list".to_string()))?;
let x = coords.first().and_then(|v| v.as_float()).unwrap_or(0.0);
let y = coords.get(1).and_then(|v| v.as_float()).unwrap_or(0.0);
let z = coords.get(2).and_then(|v| v.as_float()).unwrap_or(0.0);
Ok(Point3::new(x, y, z))
}
#[inline]
pub(super) fn parse_direction(&self, direction_entity: &DecodedEntity) -> Result<Vector3<f64>> {
if direction_entity.ifc_type != IfcType::IfcDirection {
return Err(Error::geometry(format!(
"Expected IfcDirection, got {}",
direction_entity.ifc_type
)));
}
let ratios_attr = direction_entity
.get(0)
.ok_or_else(|| Error::geometry("IfcDirection missing ratios".to_string()))?;
let ratios = ratios_attr
.as_list()
.ok_or_else(|| Error::geometry("Expected ratio list".to_string()))?;
let x = ratios.first().and_then(|v| v.as_float()).unwrap_or(0.0);
let y = ratios.get(1).and_then(|v| v.as_float()).unwrap_or(0.0);
let z = ratios.get(2).and_then(|v| v.as_float()).unwrap_or(0.0);
Ok(Vector3::new(x, y, z))
}
#[inline]
pub(super) fn parse_cartesian_transformation_operator(
&self,
entity: &DecodedEntity,
decoder: &mut EntityDecoder,
) -> Result<Matrix4<f64>> {
let origin = if let Some(origin_attr) = entity.get(2) {
if !origin_attr.is_null() {
if let Some(origin_entity) = decoder.resolve_ref(origin_attr)? {
if origin_entity.ifc_type == IfcType::IfcCartesianPoint {
let coords_attr = origin_entity.get(0);
if let Some(coords) = coords_attr.and_then(|a| a.as_list()) {
Point3::new(
coords.first().and_then(|v| v.as_float()).unwrap_or(0.0),
coords.get(1).and_then(|v| v.as_float()).unwrap_or(0.0),
coords.get(2).and_then(|v| v.as_float()).unwrap_or(0.0),
)
} else {
Point3::origin()
}
} else {
Point3::origin()
}
} else {
Point3::origin()
}
} else {
Point3::origin()
}
} else {
Point3::origin()
};
let scale = entity.get_float(3).unwrap_or(1.0);
let x_axis = if let Some(axis1_attr) = entity.get(0) {
if !axis1_attr.is_null() {
if let Some(axis1_entity) = decoder.resolve_ref(axis1_attr)? {
self.parse_direction(&axis1_entity)?.normalize()
} else {
Vector3::new(1.0, 0.0, 0.0)
}
} else {
Vector3::new(1.0, 0.0, 0.0)
}
} else {
Vector3::new(1.0, 0.0, 0.0)
};
let z_axis = if let Some(axis3_attr) = entity.get(4) {
if !axis3_attr.is_null() {
if let Some(axis3_entity) = decoder.resolve_ref(axis3_attr)? {
self.parse_direction(&axis3_entity)?.normalize()
} else {
Vector3::new(0.0, 0.0, 1.0)
}
} else {
Vector3::new(0.0, 0.0, 1.0)
}
} else {
Vector3::new(0.0, 0.0, 1.0)
};
let y_axis = z_axis.cross(&x_axis).normalize();
let x_axis = y_axis.cross(&z_axis).normalize();
let mut transform = Matrix4::identity();
transform[(0, 0)] = x_axis.x * scale;
transform[(1, 0)] = x_axis.y * scale;
transform[(2, 0)] = x_axis.z * scale;
transform[(0, 1)] = y_axis.x * scale;
transform[(1, 1)] = y_axis.y * scale;
transform[(2, 1)] = y_axis.z * scale;
transform[(0, 2)] = z_axis.x * scale;
transform[(1, 2)] = z_axis.y * scale;
transform[(2, 2)] = z_axis.z * scale;
transform[(0, 3)] = origin.x;
transform[(1, 3)] = origin.y;
transform[(2, 3)] = origin.z;
Ok(transform)
}
#[inline]
pub(super) fn transform_mesh(&self, mesh: &mut Mesh, transform: &Matrix4<f64>) {
let rtc = self.rtc_offset;
const LARGE_COORD_THRESHOLD: f64 = 1000.0;
let tx = transform[(0, 3)];
let ty = transform[(1, 3)];
let tz = transform[(2, 3)];
let needs_rtc = self.has_rtc_offset()
&& (tx.abs() > LARGE_COORD_THRESHOLD
|| ty.abs() > LARGE_COORD_THRESHOLD
|| tz.abs() > LARGE_COORD_THRESHOLD);
if needs_rtc {
mesh.positions.chunks_exact_mut(3).for_each(|chunk| {
let point = Point3::new(chunk[0] as f64, chunk[1] as f64, chunk[2] as f64);
let t = transform.transform_point(&point);
chunk[0] = (t.x - rtc.0) as f32;
chunk[1] = (t.y - rtc.1) as f32;
chunk[2] = (t.z - rtc.2) as f32;
});
} else {
mesh.positions.chunks_exact_mut(3).for_each(|chunk| {
let point = Point3::new(chunk[0] as f64, chunk[1] as f64, chunk[2] as f64);
let t = transform.transform_point(&point);
chunk[0] = t.x as f32;
chunk[1] = t.y as f32;
chunk[2] = t.z as f32;
});
}
let rotation = transform.fixed_view::<3, 3>(0, 0);
mesh.normals.chunks_exact_mut(3).for_each(|chunk| {
let normal = Vector3::new(chunk[0] as f64, chunk[1] as f64, chunk[2] as f64);
let t = (rotation * normal).normalize();
chunk[0] = t.x as f32;
chunk[1] = t.y as f32;
chunk[2] = t.z as f32;
});
}
}