use crate::error::GeometryResult;
use crate::resource::point::CartesianPoint;
use crate::resource::resolve;
use crate::slots::Slots;
use ifc_model::{Entity, EntityId, Model};
pub(crate) mod slot {
pub const PNT: usize = 0;
pub const DIR: usize = 1;
}
#[derive(Debug, Clone, Copy)]
pub struct Line<'m> {
slots: Slots<'m>,
}
impl<'m> Line<'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 point_ref(&self) -> GeometryResult<EntityId> {
self.slots.req_ref(slot::PNT, "Pnt")
}
pub fn point<'v>(&self, model: &'v Model) -> GeometryResult<CartesianPoint<'v>> {
resolve::cartesian_point(model, self.id(), self.point_ref()?)
}
pub fn direction_vector_ref(&self) -> GeometryResult<EntityId> {
self.slots.req_ref(slot::DIR, "Dir")
}
}
#[cfg(test)]
mod tests {
use super::*;
use ifc_model::Value;
fn line() -> Entity {
Entity::new(
"IFCLINE",
vec![Value::Ref(EntityId(10)), Value::Ref(EntityId(11))],
)
}
#[test]
fn reads_origin_point_and_direction_vector_from_their_own_slots() {
let e = line();
let view = Line::new(EntityId(1), &e);
assert_eq!(view.point_ref().unwrap(), EntityId(10));
assert_eq!(view.direction_vector_ref().unwrap(), EntityId(11));
}
#[test]
fn a_line_missing_its_direction_reports_the_attribute_by_name() {
let e = Entity::new("IFCLINE", vec![Value::Ref(EntityId(10))]);
let view = Line::new(EntityId(7), &e);
let err = view.direction_vector_ref().unwrap_err();
assert!(err.to_string().contains("Dir"), "got: {err}");
assert!(err.to_string().contains("#7"), "got: {err}");
}
#[test]
fn the_origin_point_resolves_to_its_coordinates() {
let mut model = Model::new();
model.insert(
EntityId(10),
Entity::new(
"IFCCARTESIANPOINT",
vec![Value::List(vec![Value::Real(3.0), Value::Real(4.0)])],
),
);
let e = line();
let point = Line::new(EntityId(1), &e).point(&model).unwrap();
assert_eq!(point.id(), EntityId(10));
assert_eq!(point.coordinates().unwrap(), vec![3.0, 4.0]);
}
#[test]
fn an_origin_that_is_not_a_point_is_a_typed_error() {
let mut model = Model::new();
model.insert(EntityId(10), Entity::new("IFCDIRECTION", vec![]));
let e = line();
let err = Line::new(EntityId(1), &e).point(&model).unwrap_err();
assert!(matches!(
err,
crate::GeometryError::WrongEntityType {
entity: EntityId(10),
..
}
));
let err = Line::new(EntityId(1), &e).point(&Model::new()).unwrap_err();
assert_eq!(err.entity(), Some(EntityId(1)), "dangling names the line");
}
#[test]
fn id_is_carried_through_for_error_attribution() {
let e = line();
assert_eq!(Line::new(EntityId(42), &e).id(), EntityId(42));
}
}