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(
58 tx: &mut Transaction,
59 distance_along: f64,
60 offsets: (Option<f64>, Option<f64>, Option<f64>),
61 basis_curve: EntityId,
62) -> Result<EntityId, AlignmentError> {
63 finite(
64 "IFCPOINTBYDISTANCEEXPRESSION",
65 "DistanceAlong",
66 distance_along,
67 )?;
68 let (lateral, vertical, longitudinal) = offsets;
69 for (name, value) in [
70 ("OffsetLateral", lateral),
71 ("OffsetVertical", vertical),
72 ("OffsetLongitudinal", longitudinal),
73 ] {
74 if let Some(value) = value {
75 finite("IFCPOINTBYDISTANCEEXPRESSION", name, value)?;
76 }
77 }
78 let mut attrs = vec![Value::Null; slot::point_by_distance::ARITY];
79 attrs[slot::point_by_distance::DISTANCE_ALONG] =
80 typed("IFCLENGTHMEASURE", Value::Real(distance_along));
81 attrs[slot::point_by_distance::OFFSET_LATERAL] = lateral.map_or(Value::Null, Value::Real);
82 attrs[slot::point_by_distance::OFFSET_VERTICAL] = vertical.map_or(Value::Null, Value::Real);
83 attrs[slot::point_by_distance::OFFSET_LONGITUDINAL] =
84 longitudinal.map_or(Value::Null, Value::Real);
85 attrs[slot::point_by_distance::BASIS_CURVE] = Value::Ref(basis_curve);
86 Ok(tx.create(Entity::new("IFCPOINTBYDISTANCEEXPRESSION", attrs)))
87}
88
89pub fn axis2_placement_linear(
96 tx: &mut Transaction,
97 location: EntityId,
98 axis: Option<EntityId>,
99 ref_direction: Option<EntityId>,
100) -> Result<EntityId, AlignmentError> {
101 let mut attrs = vec![Value::Null; slot::axis2_placement_linear::ARITY];
102 attrs[slot::axis2_placement_linear::LOCATION] = Value::Ref(location);
103 attrs[slot::axis2_placement_linear::AXIS] = axis.map_or(Value::Null, Value::Ref);
104 attrs[slot::axis2_placement_linear::REF_DIRECTION] =
105 ref_direction.map_or(Value::Null, Value::Ref);
106 Ok(tx.create(Entity::new("IFCAXIS2PLACEMENTLINEAR", attrs)))
107}
108
109pub fn linear_placement(
120 tx: &mut Transaction,
121 relative_placement: EntityId,
122 placement_rel_to: Option<EntityId>,
123 cartesian_position: Option<EntityId>,
124) -> Result<EntityId, AlignmentError> {
125 let mut attrs = vec![Value::Null; slot::linear_placement::ARITY];
126 attrs[slot::linear_placement::PLACEMENT_REL_TO] =
127 placement_rel_to.map_or(Value::Null, Value::Ref);
128 attrs[slot::linear_placement::RELATIVE_PLACEMENT] = Value::Ref(relative_placement);
129 attrs[slot::linear_placement::CARTESIAN_POSITION] =
130 cartesian_position.map_or(Value::Null, Value::Ref);
131 Ok(tx.create(Entity::new("IFCLINEARPLACEMENT", attrs)))
132}
133
134pub fn referent(
140 tx: &mut Transaction,
141 global_id: &str,
142 name: Option<&str>,
143 predefined_type: Option<&str>,
144 placement: Option<EntityId>,
145) -> Result<EntityId, AlignmentError> {
146 let mut attrs = vec![Value::Null; slot::referent::ARITY];
147 attrs[slot::product::GLOBAL_ID] = guid("IFCREFERENT", global_id)?;
148 if let Some(name) = name {
149 attrs[slot::product::NAME] = Value::Text(name.into());
150 }
151 attrs[slot::product::OBJECT_PLACEMENT] = placement.map_or(Value::Null, Value::Ref);
152 attrs[slot::referent::PREDEFINED_TYPE] =
153 predefined_type.map_or(Value::Null, |token| Value::Enum(token.into()));
154 Ok(tx.create(Entity::new("IFCREFERENT", attrs)))
155}
156
157pub fn stationing(
174 tx: &mut Transaction,
175 pset_global_id: &str,
176 rel_global_id: &str,
177 referent: EntityId,
178 station: f64,
179 incoming_station: Option<f64>,
180 has_increasing_station: Option<bool>,
181) -> Result<EntityId, AlignmentError> {
182 finite("IFCPROPERTYSET", "Station", station)?;
183 if let Some(value) = incoming_station {
184 finite("IFCPROPERTYSET", "IncomingStation", value)?;
185 }
186 let pset_guid = guid("IFCPROPERTYSET", pset_global_id)?;
187 let rel_guid = guid("IFCRELDEFINESBYPROPERTIES", rel_global_id)?;
188
189 let length = |value| typed("IFCLENGTHMEASURE", Value::Real(value));
190 let mut properties = vec![single_value(tx, "Station", length(station))];
191 if let Some(value) = incoming_station {
192 properties.push(single_value(tx, "IncomingStation", length(value)));
193 }
194 if let Some(value) = has_increasing_station {
195 let flag = typed("IFCBOOLEAN", Value::Bool(value));
196 properties.push(single_value(tx, "HasIncreasingStation", flag));
197 }
198
199 let mut pset_attrs = vec![Value::Null; 5];
200 pset_attrs[0] = pset_guid;
201 pset_attrs[2] = Value::Text("Pset_Stationing".into());
202 pset_attrs[4] = Value::List(properties.into_iter().map(Value::Ref).collect());
203 let pset = tx.create(Entity::new("IFCPROPERTYSET", pset_attrs));
204
205 let mut rel_attrs = vec![Value::Null; 6];
206 rel_attrs[0] = rel_guid;
207 rel_attrs[4] = Value::List(vec![Value::Ref(referent)]);
208 rel_attrs[5] = Value::Ref(pset);
209 Ok(tx.create(Entity::new("IFCRELDEFINESBYPROPERTIES", rel_attrs)))
210}
211
212fn typed(member: &str, value: Value) -> Value {
214 Value::Typed {
215 type_name: member.into(),
216 value: Box::new(value),
217 }
218}
219
220fn single_value(tx: &mut Transaction, name: &str, value: Value) -> EntityId {
227 let mut attrs = vec![Value::Null; 4];
228 attrs[0] = Value::Text(name.into());
229 attrs[2] = value;
230 tx.create(Entity::new("IFCPROPERTYSINGLEVALUE", attrs))
231}