Skip to main content

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, 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 `IfcRelFlowControlElements`: controls bound to a flow element.
116///
117/// IFC4 and IFC4X3 only: it writes their layout and leaves
118/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
119/// [`control_flow_element_with_owner_history`](super::control_flow_element_with_owner_history), which binds the model's declared
120/// release.
121///
122/// # Errors
123///
124/// Refuses a malformed GlobalId, an empty control set, and the flow
125/// element listed among its own controls.
126pub fn control_flow_element(
127    tx: &mut Transaction,
128    global_id: &str,
129    flow_element: EntityId,
130    controls: &[EntityId],
131) -> SpatialAuthoringResult<EntityId> {
132    super::relate(tx, FLOW_CONTROL_ELEMENTS, global_id, flow_element, controls)
133}
134
135/// Stage an `IfcRelAssignsToActor`: objects assigned to an actor.
136///
137/// Bound to the model's declared release (#213): the record is laid out by
138/// attribute name from that release's table, with all eight attributes
139/// (`ActingRole` unset). `OwnerHistory` is left `$`, which IFC4 and IFC4X3
140/// allow and IFC2X3 does not; in IFC2X3 use
141/// [`assign_to_actor_with_owner_history`](super::assign_to_actor_with_owner_history).
142///
143/// # Errors
144///
145/// Refuses a malformed GlobalId, an empty object set, and the actor
146/// listed among the objects assigned to it. A header binding no single
147/// verified release (`MultipleSchemas`, `UnsupportedSchema`) and an IFC2X3
148/// model (`AuthoringRequired`) are refused. Nothing is staged on an error.
149pub fn assign_to_actor(
150    tx: &mut Transaction,
151    model: &Model,
152    global_id: &str,
153    actor: EntityId,
154    objects: &[EntityId],
155) -> SpatialAuthoringResult<EntityId> {
156    relate_owned(
157        tx,
158        model,
159        ASSIGNS_TO_ACTOR,
160        global_id,
161        actor,
162        objects,
163        Vec::new(),
164        None,
165    )
166}
167
168/// Stage an `IfcRelAssignsToProduct`: objects assigned to a product.
169///
170/// IFC4 and IFC4X3 only: it writes their layout and leaves
171/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
172/// [`assign_to_product_with_owner_history`](super::assign_to_product_with_owner_history), which binds the model's declared
173/// release.
174///
175/// # Errors
176///
177/// Refuses a malformed GlobalId, an empty object set, and the product
178/// listed among the objects assigned to it.
179pub fn assign_to_product(
180    tx: &mut Transaction,
181    global_id: &str,
182    product: EntityId,
183    objects: &[EntityId],
184) -> SpatialAuthoringResult<EntityId> {
185    super::relate(tx, ASSIGNS_TO_PRODUCT, global_id, product, objects)
186}
187
188/// Stage an `IfcRelAssignsToProcess`: objects assigned to a process.
189///
190/// Bound to the model's declared release (#213): the record is laid out by
191/// attribute name from that release's table, with all eight attributes
192/// (`QuantityInProcess` unset). `OwnerHistory` is left `$`, which IFC4 and IFC4X3
193/// allow and IFC2X3 does not; in IFC2X3 use
194/// [`assign_to_process_with_owner_history`](super::assign_to_process_with_owner_history).
195///
196/// # Errors
197///
198/// Refuses a malformed GlobalId, an empty object set, and the process
199/// listed among the objects assigned to it. A header binding no single
200/// verified release (`MultipleSchemas`, `UnsupportedSchema`) and an IFC2X3
201/// model (`AuthoringRequired`) are refused. Nothing is staged on an error.
202pub fn assign_to_process(
203    tx: &mut Transaction,
204    model: &Model,
205    global_id: &str,
206    process: EntityId,
207    objects: &[EntityId],
208) -> SpatialAuthoringResult<EntityId> {
209    relate_owned(
210        tx,
211        model,
212        ASSIGNS_TO_PROCESS,
213        global_id,
214        process,
215        objects,
216        Vec::new(),
217        None,
218    )
219}
220
221/// Stage an `IfcRelAssignsToGroupByFactor`.
222///
223/// The `factor` is an `IfcRatioMeasure` at slot 7, scaling each
224/// member's contribution to the group.
225///
226/// IFC4 and IFC4X3 only: it writes their layout and leaves
227/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
228/// [`assign_to_group_by_factor_with_owner_history`](super::assign_to_group_by_factor_with_owner_history), which binds the model's declared
229/// release.
230///
231/// # Errors
232///
233/// Refuses a malformed GlobalId, an empty member set, the group
234/// listed among its own members, and a non-finite factor.
235pub fn assign_to_group_by_factor(
236    tx: &mut Transaction,
237    global_id: &str,
238    group: EntityId,
239    members: &[EntityId],
240    factor: f64,
241) -> SpatialAuthoringResult<EntityId> {
242    check_factor(factor)?;
243    let id = super::relate(tx, ASSIGNS_TO_GROUP_BY_FACTOR, global_id, group, members)?;
244    tx.set_attribute(id, FACTOR_SLOT, Value::Real(factor));
245    Ok(id)
246}
247
248/// Refuse a non-finite `IfcRatioMeasure` factor.
249pub(super) fn check_factor(factor: f64) -> SpatialAuthoringResult<()> {
250    if factor.is_finite() {
251        return Ok(());
252    }
253    Err(invalid(
254        ASSIGNS_TO_GROUP_BY_FACTOR.type_name,
255        "Factor",
256        format!("expected a finite ratio, got {factor}"),
257    ))
258}
259
260/// `Factor` on `IfcRelAssignsToGroupByFactor`, after the six
261/// inherited `IfcRelAssigns` attributes and `RelatingGroup`.
262const FACTOR_SLOT: usize = 7;
263
264/// Stage an `IfcRelVoidsElement`: an opening cut into an element.
265///
266/// IFC4 and IFC4X3 only: it writes their layout and leaves
267/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
268/// [`void_element_with_owner_history`](super::void_element_with_owner_history), which binds the model's declared
269/// release.
270///
271/// # Errors
272///
273/// Refuses a malformed GlobalId and an element voided by itself.
274pub fn void_element(
275    tx: &mut Transaction,
276    global_id: &str,
277    element: EntityId,
278    opening: EntityId,
279) -> SpatialAuthoringResult<EntityId> {
280    super::relate_one(tx, VOIDS_ELEMENT, global_id, element, opening)
281}
282
283/// Stage an `IfcRelFillsElement`: an element filling an opening.
284///
285/// Note the direction. The *opening* is the relating end here, the
286/// inverse of [`void_element`]: a wall is voided by an opening, and
287/// that opening is filled by a door.
288///
289/// IFC4 and IFC4X3 only: it writes their layout and leaves
290/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
291/// [`fill_element_with_owner_history`](super::fill_element_with_owner_history), which binds the model's declared
292/// release.
293///
294/// # Errors
295///
296/// Refuses a malformed GlobalId and an opening filled by itself.
297pub fn fill_element(
298    tx: &mut Transaction,
299    global_id: &str,
300    opening: EntityId,
301    filling: EntityId,
302) -> SpatialAuthoringResult<EntityId> {
303    super::relate_one(tx, FILLS_ELEMENT, global_id, opening, filling)
304}
305
306/// Stage an `IfcRelProjectsElement`: a feature added to an element.
307///
308/// IFC4 and IFC4X3 only: it writes their layout and leaves
309/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
310/// [`project_element_with_owner_history`](super::project_element_with_owner_history), which binds the model's declared
311/// release.
312///
313/// # Errors
314///
315/// Refuses a malformed GlobalId and an element projecting from itself.
316pub fn project_element(
317    tx: &mut Transaction,
318    global_id: &str,
319    element: EntityId,
320    feature: EntityId,
321) -> SpatialAuthoringResult<EntityId> {
322    super::relate_one(tx, PROJECTS_ELEMENT, global_id, element, feature)
323}
324
325/// Stage an `IfcRelAdheresToElement`: surface features bound to an element.
326///
327/// IFC4 and IFC4X3 only: it writes their layout and leaves
328/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
329/// [`adhere_to_element_with_owner_history`](super::adhere_to_element_with_owner_history), which binds the model's declared
330/// release.
331///
332/// # Errors
333///
334/// Refuses a malformed GlobalId, an empty feature set, and an element
335/// listed among its own features.
336pub fn adhere_to_element(
337    tx: &mut Transaction,
338    global_id: &str,
339    element: EntityId,
340    features: &[EntityId],
341) -> SpatialAuthoringResult<EntityId> {
342    super::relate(tx, ADHERES_TO_ELEMENT, global_id, element, features)
343}
344
345/// Stage an `IfcRelPositions`: products placed by a positioning element.
346///
347/// IFC4 and IFC4X3 only: it writes their layout and leaves
348/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
349/// [`position_products_with_owner_history`](super::position_products_with_owner_history), which binds the model's declared
350/// release.
351///
352/// # Errors
353///
354/// Refuses a malformed GlobalId, an empty product set, and the
355/// positioning element listed among its own products, which the
356/// schema's `NoSelfReference` rule forbids.
357pub fn position_products(
358    tx: &mut Transaction,
359    global_id: &str,
360    positioning: EntityId,
361    products: &[EntityId],
362) -> SpatialAuthoringResult<EntityId> {
363    super::relate(tx, POSITIONS, global_id, positioning, products)
364}
365
366/// Stage an `IfcRelAssignsToResource`: objects assigned to a resource.
367///
368/// IFC4 and IFC4X3 only: it writes their layout and leaves
369/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
370/// [`assign_to_resource_with_owner_history`](super::assign_to_resource_with_owner_history), which binds the model's declared
371/// release.
372///
373/// # Errors
374///
375/// Refuses a malformed GlobalId, an empty object set, and the resource
376/// listed among its own objects, which `NoSelfReference` forbids.
377pub fn assign_to_resource(
378    tx: &mut Transaction,
379    global_id: &str,
380    resource: EntityId,
381    objects: &[EntityId],
382) -> SpatialAuthoringResult<EntityId> {
383    super::relate(tx, ASSIGNS_TO_RESOURCE, global_id, resource, objects)
384}
385
386/// Stage an `IfcRelAssociatesProfileDef`: a profile associated with objects.
387///
388/// IFC4 and IFC4X3 only: it writes their layout and leaves
389/// `OwnerHistory` `$`, which IFC2X3 requires. In IFC2X3 use
390/// [`associate_profile_def_with_owner_history`](super::associate_profile_def_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 profile
396/// listed among its own objects.
397pub fn associate_profile_def(
398    tx: &mut Transaction,
399    global_id: &str,
400    profile: EntityId,
401    objects: &[EntityId],
402) -> SpatialAuthoringResult<EntityId> {
403    super::relate(tx, ASSOCIATES_PROFILE_DEF, global_id, profile, objects)
404}