ifc_spatial/authoring/relationships.rs
1//! The remaining objectified relationships, and `IfcFacility`.
2//!
3//! Most relationships here point one parent at a set of children, so
4//! they go through [`super::relate`], which already refuses an empty
5//! set and a parent listed among its own children. The pair-valued
6//! ones live in `connections.rs`.
7//!
8//! # `IfcRelDefinesByObject` reverses the usual order
9//!
10//! Slot 4 is `RelatedObjects` and slot 5 is `RelatingObject`, the
11//! opposite of `IfcRelAggregates`. The constant in `relation::slots`
12//! already encodes that, which is exactly why these writers take
13//! named `parent`/`children` arguments and resolve positions through
14//! `RelSlots` rather than indexing literals.
15
16use ifc_model::{EntityId, Transaction, Value};
17
18use crate::authoring::{invalid, SpatialAuthoringResult};
19use crate::relation::slots::{
20 ADHERES_TO_ELEMENT, ASSIGNS_TO_ACTOR, ASSIGNS_TO_GROUP_BY_FACTOR, ASSIGNS_TO_PROCESS,
21 ASSIGNS_TO_PRODUCT, ASSIGNS_TO_RESOURCE, ASSOCIATES_PROFILE_DEF, COVERS_ELEMENTS,
22 COVERS_SPACES, DECLARES, DEFINES_BY_OBJECT, FILLS_ELEMENT, FLOW_CONTROL_ELEMENTS, POSITIONS,
23 PROJECTS_ELEMENT, SERVICES_BUILDINGS, VOIDS_ELEMENT,
24};
25
26/// Stage an `IfcRelCoversBldgElements`: finishes applied to an element.
27///
28/// Kept apart from [`cover_spaces`] because the same covering can do
29/// both, and the two answer different questions: which finishes are on
30/// this wall, versus which finishes bound this space.
31///
32/// IFC4 and IFC4X3 only: it writes their layout and leaves
33/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
34/// [`cover_elements_with_owner_history`](super::cover_elements_with_owner_history), which binds the model's declared
35/// release.
36///
37/// # Errors
38///
39/// Refuses a malformed GlobalId, an empty covering set, and the
40/// element listed among its own coverings.
41pub fn cover_elements(
42 tx: &mut Transaction,
43 global_id: &str,
44 element: EntityId,
45 coverings: &[EntityId],
46) -> SpatialAuthoringResult<EntityId> {
47 super::relate(tx, COVERS_ELEMENTS, global_id, element, coverings)
48}
49
50/// Stage an `IfcRelCoversSpaces`: finishes bounding a space.
51///
52/// IFC4 and IFC4X3 only: it writes their layout and leaves
53/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
54/// [`cover_spaces_with_owner_history`](super::cover_spaces_with_owner_history), which binds the model's declared
55/// release.
56///
57/// # Errors
58///
59/// Refuses a malformed GlobalId, an empty covering set, and the space
60/// listed among its own coverings.
61pub fn cover_spaces(
62 tx: &mut Transaction,
63 global_id: &str,
64 space: EntityId,
65 coverings: &[EntityId],
66) -> SpatialAuthoringResult<EntityId> {
67 super::relate(tx, COVERS_SPACES, global_id, space, coverings)
68}
69
70/// Stage an `IfcRelDeclares`: definitions declared in a context.
71///
72/// The context is an `IfcProject` or `IfcProjectLibrary`. This is how
73/// type objects and property set templates enter a file without being
74/// attached to any occurrence.
75///
76/// IFC4 and IFC4X3 only: it writes their layout and leaves
77/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
78/// [`declare_with_owner_history`](super::declare_with_owner_history), which binds the model's declared
79/// release.
80///
81/// # Errors
82///
83/// Refuses a malformed GlobalId, an empty definition set, and the
84/// context listed among its own declarations.
85pub fn declare(
86 tx: &mut Transaction,
87 global_id: &str,
88 context: EntityId,
89 definitions: &[EntityId],
90) -> SpatialAuthoringResult<EntityId> {
91 super::relate(tx, DECLARES, global_id, context, definitions)
92}
93
94/// Stage an `IfcRelDefinesByObject`: occurrences defined by another object.
95///
96/// IFC4 and IFC4X3 only: it writes their layout and leaves
97/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
98/// [`define_by_object_with_owner_history`](super::define_by_object_with_owner_history), which binds the model's declared
99/// release.
100///
101/// # Errors
102///
103/// Refuses a malformed GlobalId, an empty object set, and the
104/// defining object listed among the objects it defines.
105pub fn define_by_object(
106 tx: &mut Transaction,
107 global_id: &str,
108 defining: EntityId,
109 defined: &[EntityId],
110) -> SpatialAuthoringResult<EntityId> {
111 super::relate(tx, DEFINES_BY_OBJECT, global_id, defining, defined)
112}
113
114/// Stage an `IfcRelServicesBuildings`: which spatial elements a system serves.
115///
116/// IFC4 and IFC4X3 only: it writes their layout and leaves
117/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
118/// [`serve_buildings_with_owner_history`](super::serve_buildings_with_owner_history), which binds the model's declared
119/// release.
120///
121/// # Errors
122///
123/// Refuses a malformed GlobalId, an empty building set, and the
124/// system listed among the buildings it serves.
125pub fn serve_buildings(
126 tx: &mut Transaction,
127 global_id: &str,
128 system: EntityId,
129 buildings: &[EntityId],
130) -> SpatialAuthoringResult<EntityId> {
131 super::relate(tx, SERVICES_BUILDINGS, global_id, system, buildings)
132}
133
134/// Stage an `IfcRelFlowControlElements`: controls bound to a flow element.
135///
136/// IFC4 and IFC4X3 only: it writes their layout and leaves
137/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
138/// [`control_flow_element_with_owner_history`](super::control_flow_element_with_owner_history), which binds the model's declared
139/// release.
140///
141/// # Errors
142///
143/// Refuses a malformed GlobalId, an empty control set, and the flow
144/// element listed among its own controls.
145pub fn control_flow_element(
146 tx: &mut Transaction,
147 global_id: &str,
148 flow_element: EntityId,
149 controls: &[EntityId],
150) -> SpatialAuthoringResult<EntityId> {
151 super::relate(tx, FLOW_CONTROL_ELEMENTS, global_id, flow_element, controls)
152}
153
154/// Stage an `IfcRelAssignsToActor`: objects assigned to an actor.
155///
156/// IFC4 and IFC4X3 only: it writes their layout and leaves
157/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
158/// [`assign_to_actor_with_owner_history`](super::assign_to_actor_with_owner_history), which binds the model's declared
159/// release.
160///
161/// # Errors
162///
163/// Refuses a malformed GlobalId, an empty object set, and the actor
164/// listed among the objects assigned to it.
165pub fn assign_to_actor(
166 tx: &mut Transaction,
167 global_id: &str,
168 actor: EntityId,
169 objects: &[EntityId],
170) -> SpatialAuthoringResult<EntityId> {
171 super::relate(tx, ASSIGNS_TO_ACTOR, global_id, actor, objects)
172}
173
174/// Stage an `IfcRelAssignsToProduct`: objects assigned to a product.
175///
176/// IFC4 and IFC4X3 only: it writes their layout and leaves
177/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
178/// [`assign_to_product_with_owner_history`](super::assign_to_product_with_owner_history), which binds the model's declared
179/// release.
180///
181/// # Errors
182///
183/// Refuses a malformed GlobalId, an empty object set, and the product
184/// listed among the objects assigned to it.
185pub fn assign_to_product(
186 tx: &mut Transaction,
187 global_id: &str,
188 product: EntityId,
189 objects: &[EntityId],
190) -> SpatialAuthoringResult<EntityId> {
191 super::relate(tx, ASSIGNS_TO_PRODUCT, global_id, product, objects)
192}
193
194/// Stage an `IfcRelAssignsToProcess`: objects assigned to a process.
195///
196/// IFC4 and IFC4X3 only: it writes their layout and leaves
197/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
198/// [`assign_to_process_with_owner_history`](super::assign_to_process_with_owner_history), which binds the model's declared
199/// release.
200///
201/// # Errors
202///
203/// Refuses a malformed GlobalId, an empty object set, and the process
204/// listed among the objects assigned to it.
205pub fn assign_to_process(
206 tx: &mut Transaction,
207 global_id: &str,
208 process: EntityId,
209 objects: &[EntityId],
210) -> SpatialAuthoringResult<EntityId> {
211 super::relate(tx, ASSIGNS_TO_PROCESS, global_id, process, objects)
212}
213
214/// Stage an `IfcRelAssignsToGroupByFactor`.
215///
216/// The `factor` is an `IfcRatioMeasure` at slot 7, scaling each
217/// member's contribution to the group.
218///
219/// IFC4 and IFC4X3 only: it writes their layout and leaves
220/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
221/// [`assign_to_group_by_factor_with_owner_history`](super::assign_to_group_by_factor_with_owner_history), which binds the model's declared
222/// release.
223///
224/// # Errors
225///
226/// Refuses a malformed GlobalId, an empty member set, the group
227/// listed among its own members, and a non-finite factor.
228pub fn assign_to_group_by_factor(
229 tx: &mut Transaction,
230 global_id: &str,
231 group: EntityId,
232 members: &[EntityId],
233 factor: f64,
234) -> SpatialAuthoringResult<EntityId> {
235 check_factor(factor)?;
236 let id = super::relate(tx, ASSIGNS_TO_GROUP_BY_FACTOR, global_id, group, members)?;
237 tx.set_attribute(id, FACTOR_SLOT, Value::Real(factor));
238 Ok(id)
239}
240
241/// Refuse a non-finite `IfcRatioMeasure` factor.
242pub(super) fn check_factor(factor: f64) -> SpatialAuthoringResult<()> {
243 if factor.is_finite() {
244 return Ok(());
245 }
246 Err(invalid(
247 ASSIGNS_TO_GROUP_BY_FACTOR.type_name,
248 "Factor",
249 format!("expected a finite ratio, got {factor}"),
250 ))
251}
252
253/// `Factor` on `IfcRelAssignsToGroupByFactor`, after the six
254/// inherited `IfcRelAssigns` attributes and `RelatingGroup`.
255const FACTOR_SLOT: usize = 7;
256
257/// Stage an `IfcRelVoidsElement`: an opening cut into an element.
258///
259/// IFC4 and IFC4X3 only: it writes their layout and leaves
260/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
261/// [`void_element_with_owner_history`](super::void_element_with_owner_history), which binds the model's declared
262/// release.
263///
264/// # Errors
265///
266/// Refuses a malformed GlobalId and an element voided by itself.
267pub fn void_element(
268 tx: &mut Transaction,
269 global_id: &str,
270 element: EntityId,
271 opening: EntityId,
272) -> SpatialAuthoringResult<EntityId> {
273 super::relate_one(tx, VOIDS_ELEMENT, global_id, element, opening)
274}
275
276/// Stage an `IfcRelFillsElement`: an element filling an opening.
277///
278/// Note the direction. The *opening* is the relating end here, the
279/// inverse of [`void_element`]: a wall is voided by an opening, and
280/// that opening is filled by a door.
281///
282/// IFC4 and IFC4X3 only: it writes their layout and leaves
283/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
284/// [`fill_element_with_owner_history`](super::fill_element_with_owner_history), which binds the model's declared
285/// release.
286///
287/// # Errors
288///
289/// Refuses a malformed GlobalId and an opening filled by itself.
290pub fn fill_element(
291 tx: &mut Transaction,
292 global_id: &str,
293 opening: EntityId,
294 filling: EntityId,
295) -> SpatialAuthoringResult<EntityId> {
296 super::relate_one(tx, FILLS_ELEMENT, global_id, opening, filling)
297}
298
299/// Stage an `IfcRelProjectsElement`: a feature added to an element.
300///
301/// IFC4 and IFC4X3 only: it writes their layout and leaves
302/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
303/// [`project_element_with_owner_history`](super::project_element_with_owner_history), which binds the model's declared
304/// release.
305///
306/// # Errors
307///
308/// Refuses a malformed GlobalId and an element projecting from itself.
309pub fn project_element(
310 tx: &mut Transaction,
311 global_id: &str,
312 element: EntityId,
313 feature: EntityId,
314) -> SpatialAuthoringResult<EntityId> {
315 super::relate_one(tx, PROJECTS_ELEMENT, global_id, element, feature)
316}
317
318/// Stage an `IfcRelAdheresToElement`: surface features bound to an element.
319///
320/// IFC4 and IFC4X3 only: it writes their layout and leaves
321/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
322/// [`adhere_to_element_with_owner_history`](super::adhere_to_element_with_owner_history), which binds the model's declared
323/// release.
324///
325/// # Errors
326///
327/// Refuses a malformed GlobalId, an empty feature set, and an element
328/// listed among its own features.
329pub fn adhere_to_element(
330 tx: &mut Transaction,
331 global_id: &str,
332 element: EntityId,
333 features: &[EntityId],
334) -> SpatialAuthoringResult<EntityId> {
335 super::relate(tx, ADHERES_TO_ELEMENT, global_id, element, features)
336}
337
338/// Stage an `IfcRelPositions`: products placed by a positioning element.
339///
340/// IFC4 and IFC4X3 only: it writes their layout and leaves
341/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
342/// [`position_products_with_owner_history`](super::position_products_with_owner_history), which binds the model's declared
343/// release.
344///
345/// # Errors
346///
347/// Refuses a malformed GlobalId, an empty product set, and the
348/// positioning element listed among its own products, which the
349/// schema's `NoSelfReference` rule forbids.
350pub fn position_products(
351 tx: &mut Transaction,
352 global_id: &str,
353 positioning: EntityId,
354 products: &[EntityId],
355) -> SpatialAuthoringResult<EntityId> {
356 super::relate(tx, POSITIONS, global_id, positioning, products)
357}
358
359/// Stage an `IfcRelAssignsToResource`: objects assigned to a resource.
360///
361/// IFC4 and IFC4X3 only: it writes their layout and leaves
362/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
363/// [`assign_to_resource_with_owner_history`](super::assign_to_resource_with_owner_history), which binds the model's declared
364/// release.
365///
366/// # Errors
367///
368/// Refuses a malformed GlobalId, an empty object set, and the resource
369/// listed among its own objects, which `NoSelfReference` forbids.
370pub fn assign_to_resource(
371 tx: &mut Transaction,
372 global_id: &str,
373 resource: EntityId,
374 objects: &[EntityId],
375) -> SpatialAuthoringResult<EntityId> {
376 super::relate(tx, ASSIGNS_TO_RESOURCE, global_id, resource, objects)
377}
378
379/// Stage an `IfcRelAssociatesProfileDef`: a profile associated with objects.
380///
381/// IFC4 and IFC4X3 only: it writes their layout and leaves
382/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
383/// [`associate_profile_def_with_owner_history`](super::associate_profile_def_with_owner_history), which binds the model's declared
384/// release.
385///
386/// # Errors
387///
388/// Refuses a malformed GlobalId, an empty object set, and the profile
389/// listed among its own objects.
390pub fn associate_profile_def(
391 tx: &mut Transaction,
392 global_id: &str,
393 profile: EntityId,
394 objects: &[EntityId],
395) -> SpatialAuthoringResult<EntityId> {
396 super::relate(tx, ASSOCIATES_PROFILE_DEF, global_id, profile, objects)
397}