ifc_geometry/curve/
line.rs1use crate::error::GeometryResult;
13use crate::resource::point::CartesianPoint;
14use crate::resource::resolve;
15use crate::slots::Slots;
16use ifc_model::{Entity, EntityId, Model};
17
18pub(crate) mod slot {
25 pub const PNT: usize = 0;
27 pub const DIR: usize = 1;
29}
30
31#[derive(Debug, Clone, Copy)]
33pub struct Line<'m> {
34 slots: Slots<'m>,
35}
36
37impl<'m> Line<'m> {
38 pub fn new(id: EntityId, entity: &'m Entity) -> Self {
40 Self {
41 slots: Slots::new(id, entity),
42 }
43 }
44
45 pub fn id(&self) -> EntityId {
47 self.slots.id()
48 }
49
50 pub fn point_ref(&self) -> GeometryResult<EntityId> {
54 self.slots.req_ref(slot::PNT, "Pnt")
55 }
56
57 pub fn point<'v>(&self, model: &'v Model) -> GeometryResult<CartesianPoint<'v>> {
62 resolve::cartesian_point(model, self.id(), self.point_ref()?)
63 }
64
65 pub fn direction_vector_ref(&self) -> GeometryResult<EntityId> {
70 self.slots.req_ref(slot::DIR, "Dir")
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77 use ifc_model::Value;
78
79 fn line() -> Entity {
80 Entity::new(
81 "IFCLINE",
82 vec![Value::Ref(EntityId(10)), Value::Ref(EntityId(11))],
83 )
84 }
85
86 #[test]
87 fn reads_origin_point_and_direction_vector_from_their_own_slots() {
88 let e = line();
89 let view = Line::new(EntityId(1), &e);
90 assert_eq!(view.point_ref().unwrap(), EntityId(10));
91 assert_eq!(view.direction_vector_ref().unwrap(), EntityId(11));
92 }
93
94 #[test]
95 fn a_line_missing_its_direction_reports_the_attribute_by_name() {
96 let e = Entity::new("IFCLINE", vec![Value::Ref(EntityId(10))]);
97 let view = Line::new(EntityId(7), &e);
98 let err = view.direction_vector_ref().unwrap_err();
99 assert!(err.to_string().contains("Dir"), "got: {err}");
100 assert!(err.to_string().contains("#7"), "got: {err}");
101 }
102
103 #[test]
104 fn the_origin_point_resolves_to_its_coordinates() {
105 let mut model = Model::new();
106 model.insert(
107 EntityId(10),
108 Entity::new(
109 "IFCCARTESIANPOINT",
110 vec![Value::List(vec![Value::Real(3.0), Value::Real(4.0)])],
111 ),
112 );
113 let e = line();
114 let point = Line::new(EntityId(1), &e).point(&model).unwrap();
115 assert_eq!(point.id(), EntityId(10));
116 assert_eq!(point.coordinates().unwrap(), vec![3.0, 4.0]);
117 }
118
119 #[test]
120 fn an_origin_that_is_not_a_point_is_a_typed_error() {
121 let mut model = Model::new();
122 model.insert(EntityId(10), Entity::new("IFCDIRECTION", vec![]));
123 let e = line();
124 let err = Line::new(EntityId(1), &e).point(&model).unwrap_err();
125 assert!(matches!(
126 err,
127 crate::GeometryError::WrongEntityType {
128 entity: EntityId(10),
129 ..
130 }
131 ));
132 let err = Line::new(EntityId(1), &e).point(&Model::new()).unwrap_err();
133 assert_eq!(err.entity(), Some(EntityId(1)), "dangling names the line");
134 }
135
136 #[test]
137 fn id_is_carried_through_for_error_attribution() {
138 let e = line();
139 assert_eq!(Line::new(EntityId(42), &e).id(), EntityId(42));
140 }
141}