1use std::collections::HashSet;
4
5use ifc_model::EntityId;
6
7use crate::error::StructuralResult;
8use crate::view::{Record, StructuralView};
9
10#[derive(Debug, Clone, PartialEq)]
12#[non_exhaustive]
13pub struct MemberConnection {
14 pub relation: EntityId,
16 pub member: EntityId,
18 pub connection: EntityId,
20 pub applied_condition: Option<EntityId>,
22 pub additional_conditions: Option<EntityId>,
24 pub supported_length: Option<f64>,
26 pub coordinate_system: Option<EntityId>,
28}
29
30impl MemberConnection {
31 #[must_use]
32 pub fn relation_id(&self) -> EntityId {
34 self.relation
35 }
36
37 pub fn member(&self) -> StructuralResult<EntityId> {
39 Ok(self.member)
40 }
41
42 pub fn connection(&self) -> StructuralResult<EntityId> {
44 Ok(self.connection)
45 }
46
47 pub fn supported_length(&self) -> StructuralResult<Option<f64>> {
49 Ok(self.supported_length)
50 }
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
55#[non_exhaustive]
56pub struct ActivityAssignment {
57 pub relation: EntityId,
59 pub target: EntityId,
61 pub activity: EntityId,
63}
64
65impl<'m, 's> StructuralView<'m, 's> {
66 pub fn analysis_items(&self, analysis_model: EntityId) -> StructuralResult<Vec<EntityId>> {
71 self.analysis_model(analysis_model)?;
72 let mut items = Vec::new();
73 for relation in self.ids_of_ancestor("IfcRelAssignsToGroup") {
74 let record = Record::new(self.model, self.schema, relation, "IfcRelAssignsToGroup")?;
75 if record.required_ref("RelatingGroup", "IfcGroup")? != analysis_model {
76 continue;
77 }
78 let related = record.required_set_refs_select(
79 "RelatedObjects",
80 "IfcObjectDefinition",
81 &["IfcObjectDefinition"],
82 1,
83 )?;
84 if related.contains(&analysis_model) {
85 return Err(crate::StructuralError::SemanticViolation {
86 entity: Some(relation),
87 rule: "IfcRelAssignsToGroup must not assign its RelatingGroup to itself",
88 });
89 }
90 items.extend(related);
91 }
92 Ok(items)
93 }
94
95 pub fn reactions_for_result_group(
97 &self,
98 result_group: EntityId,
99 ) -> StructuralResult<Vec<EntityId>> {
100 self.result_group(result_group)?;
101 let mut reactions = Vec::new();
102 for relation in self.ids_of_ancestor("IfcRelAssignsToGroup") {
103 let record = Record::new(self.model, self.schema, relation, "IfcRelAssignsToGroup")?;
104 if record.required_ref("RelatingGroup", "IfcGroup")? != result_group {
105 continue;
106 }
107 reactions.extend(record.required_set_refs_select(
108 "RelatedObjects",
109 "IfcStructuralReaction",
110 &["IfcStructuralReaction"],
111 1,
112 )?);
113 }
114 Ok(reactions)
115 }
116
117 pub fn member_connections(&self, member: EntityId) -> StructuralResult<Vec<MemberConnection>> {
119 self.member(member)?;
120 let mut connections = Vec::new();
121 for relation in self.ids_of_ancestor("IfcRelConnectsStructuralMember") {
122 let record = Record::new(
123 self.model,
124 self.schema,
125 relation,
126 "IfcRelConnectsStructuralMember",
127 )?;
128 let relating =
129 record.required_ref("RelatingStructuralMember", "IfcStructuralMember")?;
130 if relating != member {
131 continue;
132 }
133 connections.push(MemberConnection {
134 relation,
135 member: relating,
136 connection: record
137 .required_ref("RelatedStructuralConnection", "IfcStructuralConnection")?,
138 applied_condition: record
139 .optional_ref("AppliedCondition", "IfcBoundaryCondition")?,
140 additional_conditions: record
141 .optional_ref("AdditionalConditions", "IfcStructuralConnectionCondition")?,
142 supported_length: record.optional_number("SupportedLength")?,
143 coordinate_system: record
144 .optional_ref("ConditionCoordinateSystem", "IfcAxis2Placement3D")?,
145 });
146 }
147 Ok(connections)
148 }
149
150 pub fn activities_for(&self, target: EntityId) -> StructuralResult<Vec<ActivityAssignment>> {
157 let target_entity = self
158 .model
159 .get(target)
160 .ok_or(crate::StructuralError::EntityNotFound { id: target })?;
161 if !self.schema.is_a(&target_entity.type_name, "IfcElement")
162 && !self
163 .schema
164 .is_a(&target_entity.type_name, "IfcStructuralItem")
165 {
166 return Err(crate::StructuralError::WrongType {
167 id: target,
168 expected: "IfcStructuralActivityAssignmentSelect",
169 actual: target_entity.type_name.to_string(),
170 });
171 }
172 let mut assignments = Vec::new();
173 let mut attached_activities = HashSet::new();
174 for relation in self.ids_of_ancestor("IfcRelConnectsStructuralActivity") {
175 let record = Record::new(
176 self.model,
177 self.schema,
178 relation,
179 "IfcRelConnectsStructuralActivity",
180 )?;
181 let relating = record.required_ref_select(
182 "RelatingElement",
183 "IfcStructuralActivityAssignmentSelect",
184 &["IfcElement", "IfcStructuralItem"],
185 )?;
186 let activity =
187 record.required_ref("RelatedStructuralActivity", "IfcStructuralActivity")?;
188 if !attached_activities.insert(activity) {
189 return Err(crate::StructuralError::SemanticViolation {
190 entity: Some(relation),
191 rule: "IfcStructuralActivity must have at most one attachment relation",
192 });
193 }
194 if relating != target {
195 continue;
196 }
197 assignments.push(ActivityAssignment {
198 relation,
199 target: relating,
200 activity,
201 });
202 }
203 Ok(assignments)
204 }
205}