1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//! Bounded structural relationship queries.
use std::collections::HashSet;
use ifc_model::EntityId;
use crate::error::StructuralResult;
use crate::view::{Record, StructuralView};
/// A resolved `IfcRelConnectsStructuralMember` between one member and its connection.
#[derive(Debug, Clone, PartialEq)]
pub struct MemberConnection {
/// The `IfcRelConnectsStructuralMember` relation entity itself.
pub relation: EntityId,
/// `RelatingStructuralMember`.
pub member: EntityId,
/// `RelatedStructuralConnection`.
pub connection: EntityId,
/// `AppliedCondition`, an `IfcBoundaryCondition` overriding the connection's own condition. Legally absent.
pub applied_condition: Option<EntityId>,
/// `AdditionalConditions`, an `IfcStructuralConnectionCondition`. Legally absent.
pub additional_conditions: Option<EntityId>,
/// `SupportedLength`, the tributary length this connection carries along the member. Legally absent.
pub supported_length: Option<f64>,
/// `ConditionCoordinateSystem`, an `IfcAxis2Placement3D` local to the connection. Legally absent.
pub coordinate_system: Option<EntityId>,
}
impl MemberConnection {
#[must_use]
/// The `IfcRelConnectsStructuralMember` entity id backing this connection.
pub fn relation_id(&self) -> EntityId {
self.relation
}
/// The connected `IfcStructuralMember` (`RelatingStructuralMember`). Always present.
pub fn member(&self) -> StructuralResult<EntityId> {
Ok(self.member)
}
/// The connected `IfcStructuralConnection` (`RelatedStructuralConnection`). Always present.
pub fn connection(&self) -> StructuralResult<EntityId> {
Ok(self.connection)
}
/// `SupportedLength`, legally absent when the whole member is supported.
pub fn supported_length(&self) -> StructuralResult<Option<f64>> {
Ok(self.supported_length)
}
}
/// A resolved `IfcRelConnectsStructuralActivity` attaching one activity (action or reaction) to its target.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ActivityAssignment {
/// The `IfcRelConnectsStructuralActivity` relation entity itself.
pub relation: EntityId,
/// `RelatingElement`, the `IfcStructuralActivityAssignmentSelect` (an `IfcElement` or `IfcStructuralItem`) the activity is attached to.
pub target: EntityId,
/// `RelatedStructuralActivity`, the attached `IfcStructuralActivity` (action or reaction).
pub activity: EntityId,
}
impl<'m, 's> StructuralView<'m, 's> {
/// Members of `analysis_model`'s `IsGroupedBy` `IfcRelAssignsToGroup` relations, in file order.
///
/// Fails with [`crate::StructuralError::SemanticViolation`] if a relation
/// assigns the analysis model to itself.
pub fn analysis_items(&self, analysis_model: EntityId) -> StructuralResult<Vec<EntityId>> {
self.analysis_model(analysis_model)?;
let mut items = Vec::new();
for relation in self.ids_of_ancestor("IfcRelAssignsToGroup") {
let record = Record::new(self.model, self.schema, relation, "IfcRelAssignsToGroup")?;
if record.required_ref("RelatingGroup", "IfcGroup")? != analysis_model {
continue;
}
let related = record.required_set_refs_select(
"RelatedObjects",
"IfcObjectDefinition",
&["IfcObjectDefinition"],
1,
)?;
if related.contains(&analysis_model) {
return Err(crate::StructuralError::SemanticViolation {
entity: Some(relation),
rule: "IfcRelAssignsToGroup must not assign its RelatingGroup to itself",
});
}
items.extend(related);
}
Ok(items)
}
/// Reactions assigned to one structural result group in relation/file order.
pub fn reactions_for_result_group(
&self,
result_group: EntityId,
) -> StructuralResult<Vec<EntityId>> {
self.result_group(result_group)?;
let mut reactions = Vec::new();
for relation in self.ids_of_ancestor("IfcRelAssignsToGroup") {
let record = Record::new(self.model, self.schema, relation, "IfcRelAssignsToGroup")?;
if record.required_ref("RelatingGroup", "IfcGroup")? != result_group {
continue;
}
reactions.extend(record.required_set_refs_select(
"RelatedObjects",
"IfcStructuralReaction",
&["IfcStructuralReaction"],
1,
)?);
}
Ok(reactions)
}
/// `IfcRelConnectsStructuralMember` relations attaching connections to `member`, in file order.
pub fn member_connections(&self, member: EntityId) -> StructuralResult<Vec<MemberConnection>> {
self.member(member)?;
let mut connections = Vec::new();
for relation in self.ids_of_ancestor("IfcRelConnectsStructuralMember") {
let record = Record::new(
self.model,
self.schema,
relation,
"IfcRelConnectsStructuralMember",
)?;
let relating =
record.required_ref("RelatingStructuralMember", "IfcStructuralMember")?;
if relating != member {
continue;
}
connections.push(MemberConnection {
relation,
member: relating,
connection: record
.required_ref("RelatedStructuralConnection", "IfcStructuralConnection")?,
applied_condition: record
.optional_ref("AppliedCondition", "IfcBoundaryCondition")?,
additional_conditions: record
.optional_ref("AdditionalConditions", "IfcStructuralConnectionCondition")?,
supported_length: record.optional_number("SupportedLength")?,
coordinate_system: record
.optional_ref("ConditionCoordinateSystem", "IfcAxis2Placement3D")?,
});
}
Ok(connections)
}
/// `IfcRelConnectsStructuralActivity` relations attaching activities to `target`, in file order.
///
/// Fails with [`crate::StructuralError::WrongType`] if `target` is neither
/// an `IfcElement` nor an `IfcStructuralItem`, and with
/// [`crate::StructuralError::SemanticViolation`] if an `IfcStructuralActivity`
/// is attached by more than one relation.
pub fn activities_for(&self, target: EntityId) -> StructuralResult<Vec<ActivityAssignment>> {
let target_entity = self
.model
.get(target)
.ok_or(crate::StructuralError::EntityNotFound { id: target })?;
if !self.schema.is_a(&target_entity.type_name, "IfcElement")
&& !self
.schema
.is_a(&target_entity.type_name, "IfcStructuralItem")
{
return Err(crate::StructuralError::WrongType {
id: target,
expected: "IfcStructuralActivityAssignmentSelect",
actual: target_entity.type_name.to_string(),
});
}
let mut assignments = Vec::new();
let mut attached_activities = HashSet::new();
for relation in self.ids_of_ancestor("IfcRelConnectsStructuralActivity") {
let record = Record::new(
self.model,
self.schema,
relation,
"IfcRelConnectsStructuralActivity",
)?;
let relating = record.required_ref_select(
"RelatingElement",
"IfcStructuralActivityAssignmentSelect",
&["IfcElement", "IfcStructuralItem"],
)?;
let activity =
record.required_ref("RelatedStructuralActivity", "IfcStructuralActivity")?;
if !attached_activities.insert(activity) {
return Err(crate::StructuralError::SemanticViolation {
entity: Some(relation),
rule: "IfcStructuralActivity must have at most one attachment relation",
});
}
if relating != target {
continue;
}
assignments.push(ActivityAssignment {
relation,
target: relating,
activity,
});
}
Ok(assignments)
}
}