use axiolid_core::Vec3;
use axiolid_model::StationOrientation;
use ifc_model::EntityId;
use super::PLACEMENT;
use crate::error::GeometryResult;
use crate::lower::session::LoweringSession;
mod slot {
pub const LOCATION: usize = 0;
pub const AXIS: usize = 1;
pub const REF_DIRECTION: usize = 2;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum SectionOf {
SolidHorizontal,
Surface,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct LinearPlacement {
pub entity: EntityId,
pub location: EntityId,
pub axis: Option<EntityId>,
pub ref_direction: Option<EntityId>,
}
pub(crate) fn read_placement(
session: &LoweringSession<'_>,
id: EntityId,
) -> GeometryResult<LinearPlacement> {
let kind = session.type_name(id)?;
if kind != PLACEMENT {
return Err(session.degenerate(
id,
&kind,
"a cross section position must be an IfcAxis2PlacementLinear",
));
}
let slots = session.slots(id)?;
Ok(LinearPlacement {
entity: id,
location: slots.req_ref(slot::LOCATION, "Location")?,
axis: slots.opt_ref(slot::AXIS),
ref_direction: slots.opt_ref(slot::REF_DIRECTION),
})
}
pub(crate) fn placement_orientation(
session: &LoweringSession<'_>,
owner: EntityId,
placement: &LinearPlacement,
) -> GeometryResult<StationOrientation> {
let axis = placement
.axis
.map(|id| direction(session, owner, id, "Axis"))
.transpose()?;
let ref_direction = placement
.ref_direction
.map(|id| direction(session, owner, id, "RefDirection"))
.transpose()?;
let orientation = StationOrientation::new(axis, ref_direction);
orientation.unit_axes().map_err(|why| {
session.degenerate(
owner,
PLACEMENT,
format!("WR2: Axis and RefDirection must not be parallel or anti-parallel ({why})"),
)
})?;
Ok(orientation)
}
pub(crate) fn section_orientation(
session: &LoweringSession<'_>,
owner: EntityId,
placement: &LinearPlacement,
) -> GeometryResult<StationOrientation> {
placement_orientation(session, owner, placement)
}
fn direction(
session: &LoweringSession<'_>,
owner: EntityId,
id: EntityId,
name: &str,
) -> GeometryResult<Vec3> {
let ratios = session.slots(id)?.req_f64_list(0, "DirectionRatios")?;
match ratios.as_slice() {
[x, y, z] => Ok(Vec3::new(*x, *y, *z)),
_ => Err(session.degenerate(
owner,
PLACEMENT,
format!(
"{name} has {} direction ratios; a linear placement's axes are components in the \
3D station frame (tangent, left, up)",
ratios.len()
),
)),
}
}