use ifc_model::{EntityId, Model};
use crate::error::{GeometryError, GeometryResult};
use crate::resource::placement::axis_placement_transform;
use crate::transform::Transform;
use crate::units::UnitScale;
mod slot {
pub const CARTESIAN_POSITION: usize = 2;
}
pub(crate) fn is_linear_placement(model: &Model, placement: EntityId) -> bool {
model
.get(placement)
.is_some_and(|entity| entity.is_type("IFCLINEARPLACEMENT"))
}
pub(crate) fn linear_placement_transform(
model: &Model,
units: &UnitScale,
placement: EntityId,
) -> GeometryResult<Transform> {
let entity = model.get(placement).ok_or(GeometryError::MissingEntity {
referrer: placement,
missing: placement,
})?;
let alignment_units = ifc_alignment::AlignmentUnits {
length_to_metres: units.length_to_metres,
angle_to_radians: units.angle_to_radians,
};
ifc_alignment::resolve_linear_placement(model, placement, alignment_units).map_err(
|_error| GeometryError::Unsupported {
entity: placement,
type_name: "IFCLINEARPLACEMENT".into(),
detail: "linear placement is malformed; ifc-alignment refused it",
},
)?;
let cached = entity
.attributes
.get(slot::CARTESIAN_POSITION)
.and_then(ifc_model::Value::as_ref_id);
let Some(cartesian) = cached else {
return Err(GeometryError::Unsupported {
entity: placement,
type_name: "IFCLINEARPLACEMENT".into(),
detail: "placement states only IfcPointByDistanceExpression and no CartesianPosition; \
derive the frame with constraint::placement::derive and a CurveEvaluator",
});
};
let cartesian_entity = model.get(cartesian).ok_or(GeometryError::MissingEntity {
referrer: placement,
missing: cartesian,
})?;
axis_placement_transform(model, cartesian, cartesian_entity)
}