ifc_structural/authoring/
condition.rs1use ifc_model::{Entity, EntityId, Transaction, Value};
18use ifc_schema::Schema;
19
20use super::build_named;
21
22use crate::condition::{AxisValues, BoundaryConditionKind, FailureLimits, StiffnessValue};
23use crate::error::{StructuralError, StructuralResult};
24
25#[derive(Debug, Clone, Copy, Default)]
27pub struct BoundaryConditionDraft<'a> {
28 pub name: Option<&'a str>,
30 pub translational: AxisValues<Option<StiffnessValue>>,
34 pub rotational: AxisValues<Option<StiffnessValue>>,
37 pub warping: Option<StiffnessValue>,
39}
40
41pub fn stage_boundary_condition(
50 tx: &mut Transaction,
51 kind: BoundaryConditionKind,
52 draft: BoundaryConditionDraft<'_>,
53) -> StructuralResult<EntityId> {
54 let entity_type = entity_name(kind);
55 if kind == BoundaryConditionKind::Face && has_any(draft.rotational) {
56 return Err(crate::error::StructuralError::InvalidDraftValue {
57 entity_type,
58 attribute: "RotationalStiffness",
59 expected: "no rotational stiffness: face conditions do not declare one",
60 });
61 }
62 if draft.warping.is_some() && kind != BoundaryConditionKind::NodeWarping {
63 return Err(crate::error::StructuralError::InvalidDraftValue {
64 entity_type,
65 attribute: "WarpingStiffness",
66 expected: "no warping stiffness outside IfcBoundaryNodeConditionWarping",
67 });
68 }
69 for (axis, value) in axes(draft.translational).chain(axes(draft.rotational)) {
70 if let Some(StiffnessValue::Measure(measure)) = value {
71 if !measure.is_finite() {
72 return Err(crate::error::StructuralError::InvalidDraftValue {
73 entity_type,
74 attribute: axis,
75 expected: "a finite stiffness measure",
76 });
77 }
78 }
79 }
80 let mut attributes = vec![optional_text(draft.name)];
81 attributes.extend(stiffness_triple(draft.translational, kind, true));
82 if kind != BoundaryConditionKind::Face {
83 attributes.extend(stiffness_triple(draft.rotational, kind, false));
84 }
85 if kind == BoundaryConditionKind::NodeWarping {
86 attributes.push(stiffness_value(draft.warping, kind, false));
87 }
88 Ok(tx.create(Entity::new(entity_type, attributes)))
89}
90
91fn entity_name(kind: BoundaryConditionKind) -> &'static str {
92 match kind {
93 BoundaryConditionKind::Edge => "IFCBOUNDARYEDGECONDITION",
94 BoundaryConditionKind::Face => "IFCBOUNDARYFACECONDITION",
95 BoundaryConditionKind::Node => "IFCBOUNDARYNODECONDITION",
96 BoundaryConditionKind::NodeWarping => "IFCBOUNDARYNODECONDITIONWARPING",
97 }
98}
99
100fn measure_name(kind: BoundaryConditionKind, translational: bool) -> &'static str {
104 match (kind, translational) {
105 (BoundaryConditionKind::Edge, true) => "IFCMODULUSOFTRANSLATIONALSUBGRADEREACTIONMEASURE",
106 (BoundaryConditionKind::Edge, false) => "IFCMODULUSOFROTATIONALSUBGRADEREACTIONMEASURE",
107 (BoundaryConditionKind::Face, _) => "IFCMODULUSOFSUBGRADEREACTIONMEASURE",
108 (_, true) => "IFCLINEARSTIFFNESSMEASURE",
109 (_, false) => "IFCROTATIONALSTIFFNESSMEASURE",
110 }
111}
112
113fn stiffness_value(
114 value: Option<StiffnessValue>,
115 kind: BoundaryConditionKind,
116 translational: bool,
117) -> Value {
118 match value {
119 None => Value::Null,
120 Some(StiffnessValue::Boolean(flag)) => Value::Bool(flag),
121 Some(StiffnessValue::Measure(measure)) => Value::Typed {
122 type_name: measure_name(kind, translational).into(),
123 value: Box::new(Value::Real(measure)),
124 },
125 }
126}
127
128fn stiffness_triple(
129 values: AxisValues<Option<StiffnessValue>>,
130 kind: BoundaryConditionKind,
131 translational: bool,
132) -> [Value; 3] {
133 [
134 stiffness_value(values.x, kind, translational),
135 stiffness_value(values.y, kind, translational),
136 stiffness_value(values.z, kind, translational),
137 ]
138}
139
140fn has_any(values: AxisValues<Option<StiffnessValue>>) -> bool {
141 values.x.is_some() || values.y.is_some() || values.z.is_some()
142}
143
144fn axes(
145 values: AxisValues<Option<StiffnessValue>>,
146) -> impl Iterator<Item = (&'static str, Option<StiffnessValue>)> {
147 [("X", values.x), ("Y", values.y), ("Z", values.z)].into_iter()
148}
149
150fn optional_text(value: Option<&str>) -> Value {
151 value.map_or(Value::Null, |text| Value::Text(text.into()))
152}
153
154#[derive(Debug, Clone, Copy)]
160pub enum ConnectionConditionDraft {
161 Failure(FailureLimits),
164 Slippage(AxisValues<Option<f64>>),
167}
168
169pub fn stage_connection_condition(
179 tx: &mut Transaction,
180 schema: &Schema,
181 name: Option<&str>,
182 draft: ConnectionConditionDraft,
183) -> StructuralResult<EntityId> {
184 let (entity, values): (&'static str, Vec<(&'static str, Value)>) = match draft {
185 ConnectionConditionDraft::Failure(limits) => (
186 "IfcFailureConnectionCondition",
187 vec![
188 ("TensionFailureX", measure(limits.tension.x)),
189 ("TensionFailureY", measure(limits.tension.y)),
190 ("TensionFailureZ", measure(limits.tension.z)),
191 ("CompressionFailureX", measure(limits.compression.x)),
192 ("CompressionFailureY", measure(limits.compression.y)),
193 ("CompressionFailureZ", measure(limits.compression.z)),
194 ],
195 ),
196 ConnectionConditionDraft::Slippage(slippage) => (
197 "IfcSlippageConnectionCondition",
198 vec![
199 ("SlippageX", measure(slippage.x)),
200 ("SlippageY", measure(slippage.y)),
201 ("SlippageZ", measure(slippage.z)),
202 ],
203 ),
204 };
205 for (attribute, value) in &values {
206 if matches!(value, Value::Real(number) if !number.is_finite()) {
207 return Err(StructuralError::InvalidDraftValue {
208 entity_type: entity,
209 attribute,
210 expected: "finite measure or null",
211 });
212 }
213 }
214 let mut fields = vec![(
215 "Name",
216 name.map_or(Value::Null, |text| Value::Text(text.into())),
217 )];
218 fields.extend(values);
219 Ok(tx.create(build_named(schema, entity, fields)?))
220}
221
222fn measure(value: Option<f64>) -> Value {
224 value.map_or(Value::Null, Value::Real)
225}