use crate::error::GeometryResult;
use crate::slots::Slots;
use ifc_model::{Entity, EntityId};
pub(crate) mod axis_slot {
pub const AXIS_TAG: usize = 0;
pub const AXIS_CURVE: usize = 1;
pub const SAME_SENSE: usize = 2;
}
pub(crate) mod intersection_slot {
pub const INTERSECTING_AXES: usize = 0;
pub const OFFSET_DISTANCES: usize = 1;
}
pub(crate) mod placement_slot {
pub const PLACEMENT_LOCATION: usize = 0;
pub const PLACEMENT_REF_DIRECTION: usize = 1;
}
#[derive(Debug, Clone, Copy)]
pub struct GridAxis<'m> {
slots: Slots<'m>,
}
impl<'m> GridAxis<'m> {
pub fn new(id: EntityId, entity: &'m Entity) -> Self {
Self {
slots: Slots::new(id, entity),
}
}
pub fn id(&self) -> EntityId {
self.slots.id()
}
pub fn tag(&self) -> Option<&'m str> {
self.slots.opt(axis_slot::AXIS_TAG)?.as_text()
}
pub fn curve(&self) -> GeometryResult<EntityId> {
self.slots.req_ref(axis_slot::AXIS_CURVE, "AxisCurve")
}
pub fn same_sense(&self) -> bool {
self.slots.opt_bool(axis_slot::SAME_SENSE).unwrap_or(true)
}
}
#[derive(Debug, Clone, Copy)]
pub struct VirtualGridIntersection<'m> {
slots: Slots<'m>,
}
impl<'m> VirtualGridIntersection<'m> {
pub fn new(id: EntityId, entity: &'m Entity) -> Self {
Self {
slots: Slots::new(id, entity),
}
}
pub fn id(&self) -> EntityId {
self.slots.id()
}
pub fn axes(&self) -> GeometryResult<Vec<EntityId>> {
self.slots
.req_ref_list(intersection_slot::INTERSECTING_AXES, "IntersectingAxes")
}
pub fn offsets(&self) -> GeometryResult<Vec<f64>> {
self.slots
.req_f64_list(intersection_slot::OFFSET_DISTANCES, "OffsetDistances")
}
}
#[derive(Debug, Clone, Copy)]
pub struct GridPlacement<'m> {
slots: Slots<'m>,
}
impl<'m> GridPlacement<'m> {
pub fn new(id: EntityId, entity: &'m Entity) -> Self {
Self {
slots: Slots::new(id, entity),
}
}
pub fn id(&self) -> EntityId {
self.slots.id()
}
pub fn location(&self) -> GeometryResult<EntityId> {
self.slots
.req_ref(placement_slot::PLACEMENT_LOCATION, "PlacementLocation")
}
pub fn ref_direction(&self) -> Option<EntityId> {
self.slots.opt_ref(placement_slot::PLACEMENT_REF_DIRECTION)
}
}
#[cfg(test)]
mod tests {
use super::*;
use ifc_model::Value;
#[test]
fn reads_axis_tag_and_curve() {
let e = Entity::new(
"IFCGRIDAXIS",
vec![
Value::Text("C".into()),
Value::Ref(EntityId(5)),
Value::Bool(true),
],
);
let axis = GridAxis::new(EntityId(1), &e);
assert_eq!(axis.tag(), Some("C"));
assert_eq!(axis.curve().unwrap(), EntityId(5));
assert!(axis.same_sense());
}
#[test]
fn absent_same_sense_defaults_to_true() {
let e = Entity::new(
"IFCGRIDAXIS",
vec![
Value::Text("4".into()),
Value::Ref(EntityId(5)),
Value::Null,
],
);
assert!(GridAxis::new(EntityId(1), &e).same_sense());
}
#[test]
fn reads_intersection_axes_and_offsets() {
let e = Entity::new(
"IFCVIRTUALGRIDINTERSECTION",
vec![
Value::List(vec![Value::Ref(EntityId(1)), Value::Ref(EntityId(2))]),
Value::List(vec![Value::Real(0.5), Value::Real(-0.25)]),
],
);
let x = VirtualGridIntersection::new(EntityId(3), &e);
assert_eq!(x.axes().unwrap().len(), 2);
assert_eq!(x.offsets().unwrap(), vec![0.5, -0.25]);
}
#[test]
fn ref_direction_is_optional() {
let e = Entity::new(
"IFCGRIDPLACEMENT",
vec![Value::Ref(EntityId(9)), Value::Null],
);
let p = GridPlacement::new(EntityId(1), &e);
assert_eq!(p.location().unwrap(), EntityId(9));
assert_eq!(p.ref_direction(), None);
}
}