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