1use ifc_model::{Entity, EntityId, Transaction, Value};
10
11use super::{finite, guid, invalid};
12use crate::error::AlignmentError;
13use crate::slot;
14
15pub fn cartesian_point(
21 tx: &mut Transaction,
22 coordinates: &[f64],
23) -> Result<EntityId, AlignmentError> {
24 if coordinates.is_empty() || coordinates.len() > 3 {
25 return Err(invalid(
26 "IFCCARTESIANPOINT",
27 "Coordinates",
28 format!("{} coordinates", coordinates.len()),
29 ));
30 }
31 for value in coordinates {
32 finite("IFCCARTESIANPOINT", "Coordinates", *value)?;
33 }
34 Ok(tx.create(Entity::new(
35 "IFCCARTESIANPOINT",
36 vec![Value::List(
37 coordinates.iter().copied().map(Value::Real).collect(),
38 )],
39 )))
40}
41
42pub fn point_by_distance(
52 tx: &mut Transaction,
53 distance_along: f64,
54 offsets: (Option<f64>, Option<f64>, Option<f64>),
55 basis_curve: EntityId,
56) -> Result<EntityId, AlignmentError> {
57 finite(
58 "IFCPOINTBYDISTANCEEXPRESSION",
59 "DistanceAlong",
60 distance_along,
61 )?;
62 let (lateral, vertical, longitudinal) = offsets;
63 for (name, value) in [
64 ("OffsetLateral", lateral),
65 ("OffsetVertical", vertical),
66 ("OffsetLongitudinal", longitudinal),
67 ] {
68 if let Some(value) = value {
69 finite("IFCPOINTBYDISTANCEEXPRESSION", name, value)?;
70 }
71 }
72 let mut attrs = vec![Value::Null; slot::point_by_distance::ARITY];
73 attrs[slot::point_by_distance::DISTANCE_ALONG] = Value::Real(distance_along);
74 attrs[slot::point_by_distance::OFFSET_LATERAL] = lateral.map_or(Value::Null, Value::Real);
75 attrs[slot::point_by_distance::OFFSET_VERTICAL] = vertical.map_or(Value::Null, Value::Real);
76 attrs[slot::point_by_distance::OFFSET_LONGITUDINAL] =
77 longitudinal.map_or(Value::Null, Value::Real);
78 attrs[slot::point_by_distance::BASIS_CURVE] = Value::Ref(basis_curve);
79 Ok(tx.create(Entity::new("IFCPOINTBYDISTANCEEXPRESSION", attrs)))
80}
81
82pub fn axis2_placement_linear(
89 tx: &mut Transaction,
90 location: EntityId,
91 axis: Option<EntityId>,
92 ref_direction: Option<EntityId>,
93) -> Result<EntityId, AlignmentError> {
94 let mut attrs = vec![Value::Null; slot::axis2_placement_linear::ARITY];
95 attrs[slot::axis2_placement_linear::LOCATION] = Value::Ref(location);
96 attrs[slot::axis2_placement_linear::AXIS] = axis.map_or(Value::Null, Value::Ref);
97 attrs[slot::axis2_placement_linear::REF_DIRECTION] =
98 ref_direction.map_or(Value::Null, Value::Ref);
99 Ok(tx.create(Entity::new("IFCAXIS2PLACEMENTLINEAR", attrs)))
100}
101
102pub fn linear_placement(
113 tx: &mut Transaction,
114 relative_placement: EntityId,
115 placement_rel_to: Option<EntityId>,
116 cartesian_position: Option<EntityId>,
117) -> Result<EntityId, AlignmentError> {
118 let mut attrs = vec![Value::Null; slot::linear_placement::ARITY];
119 attrs[slot::linear_placement::PLACEMENT_REL_TO] =
120 placement_rel_to.map_or(Value::Null, Value::Ref);
121 attrs[slot::linear_placement::RELATIVE_PLACEMENT] = Value::Ref(relative_placement);
122 attrs[slot::linear_placement::CARTESIAN_POSITION] =
123 cartesian_position.map_or(Value::Null, Value::Ref);
124 Ok(tx.create(Entity::new("IFCLINEARPLACEMENT", attrs)))
125}
126
127pub fn referent(
133 tx: &mut Transaction,
134 global_id: &str,
135 name: Option<&str>,
136 predefined_type: Option<&str>,
137 placement: Option<EntityId>,
138) -> Result<EntityId, AlignmentError> {
139 let mut attrs = vec![Value::Null; slot::referent::ARITY];
140 attrs[slot::product::GLOBAL_ID] = guid("IFCREFERENT", global_id)?;
141 if let Some(name) = name {
142 attrs[slot::product::NAME] = Value::Text(name.into());
143 }
144 attrs[slot::product::OBJECT_PLACEMENT] = placement.map_or(Value::Null, Value::Ref);
145 attrs[slot::referent::PREDEFINED_TYPE] =
146 predefined_type.map_or(Value::Null, |token| Value::Enum(token.into()));
147 Ok(tx.create(Entity::new("IFCREFERENT", attrs)))
148}
149
150pub fn stationing(
167 tx: &mut Transaction,
168 pset_global_id: &str,
169 rel_global_id: &str,
170 referent: EntityId,
171 station: f64,
172 incoming_station: Option<f64>,
173 has_increasing_station: Option<bool>,
174) -> Result<EntityId, AlignmentError> {
175 finite("IFCPROPERTYSET", "Station", station)?;
176 if let Some(value) = incoming_station {
177 finite("IFCPROPERTYSET", "IncomingStation", value)?;
178 }
179 let pset_guid = guid("IFCPROPERTYSET", pset_global_id)?;
180 let rel_guid = guid("IFCRELDEFINESBYPROPERTIES", rel_global_id)?;
181
182 let mut properties = vec![single_value(tx, "Station", Value::Real(station))];
183 if let Some(value) = incoming_station {
184 properties.push(single_value(tx, "IncomingStation", Value::Real(value)));
185 }
186 if let Some(value) = has_increasing_station {
187 properties.push(single_value(tx, "HasIncreasingStation", Value::Bool(value)));
188 }
189
190 let mut pset_attrs = vec![Value::Null; 5];
191 pset_attrs[0] = pset_guid;
192 pset_attrs[2] = Value::Text("Pset_Stationing".into());
193 pset_attrs[4] = Value::List(properties.into_iter().map(Value::Ref).collect());
194 let pset = tx.create(Entity::new("IFCPROPERTYSET", pset_attrs));
195
196 let mut rel_attrs = vec![Value::Null; 6];
197 rel_attrs[0] = rel_guid;
198 rel_attrs[4] = Value::List(vec![Value::Ref(referent)]);
199 rel_attrs[5] = Value::Ref(pset);
200 Ok(tx.create(Entity::new("IFCRELDEFINESBYPROPERTIES", rel_attrs)))
201}
202
203fn single_value(tx: &mut Transaction, name: &str, value: Value) -> EntityId {
205 let mut attrs = vec![Value::Null; 4];
206 attrs[0] = Value::Text(name.into());
207 attrs[2] = value;
208 tx.create(Entity::new("IFCPROPERTYSINGLEVALUE", attrs))
209}