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