ifc_structural/model/
analysis.rs1use ifc_model::EntityId;
4
5use crate::error::{StructuralError, StructuralResult};
6use crate::view::Record;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10#[non_exhaustive]
11pub enum AnalysisModelType {
12 InPlaneLoading2d,
14 OutPlaneLoading2d,
16 Loading3d,
18 UserDefined,
20 NotDefined,
22}
23
24impl AnalysisModelType {
25 pub(crate) fn parse(value: &str) -> Option<Self> {
26 match value.to_ascii_uppercase().as_str() {
27 "IN_PLANE_LOADING_2D" => Some(Self::InPlaneLoading2d),
28 "OUT_PLANE_LOADING_2D" => Some(Self::OutPlaneLoading2d),
29 "LOADING_3D" => Some(Self::Loading3d),
30 "USERDEFINED" => Some(Self::UserDefined),
31 "NOTDEFINED" => Some(Self::NotDefined),
32 _ => None,
33 }
34 }
35
36 pub(crate) fn token(self) -> &'static str {
37 match self {
38 Self::InPlaneLoading2d => "IN_PLANE_LOADING_2D",
39 Self::OutPlaneLoading2d => "OUT_PLANE_LOADING_2D",
40 Self::Loading3d => "LOADING_3D",
41 Self::UserDefined => "USERDEFINED",
42 Self::NotDefined => "NOTDEFINED",
43 }
44 }
45}
46
47#[derive(Debug, Clone, Copy)]
49pub struct AnalysisModel<'m, 's> {
50 record: Record<'m, 's>,
51}
52
53impl<'m, 's> AnalysisModel<'m, 's> {
54 pub(crate) fn from_record(record: Record<'m, 's>) -> Self {
55 Self { record }
56 }
57
58 #[must_use]
59 pub fn id(&self) -> EntityId {
61 self.record.id
62 }
63
64 pub fn name(&self) -> StructuralResult<Option<&'m str>> {
66 self.record.optional_text("Name")
67 }
68
69 pub fn object_type(&self) -> StructuralResult<Option<&'m str>> {
71 self.record.optional_text("ObjectType")
72 }
73
74 pub fn predefined_type(&self) -> StructuralResult<AnalysisModelType> {
81 let value = self.record.required_enum("PredefinedType")?;
82 let parsed = AnalysisModelType::parse(value).ok_or(StructuralError::InvalidValue {
83 entity: self.record.id,
84 attribute: "PredefinedType",
85 expected: "IfcAnalysisModelTypeEnum",
86 })?;
87 if parsed == AnalysisModelType::UserDefined
88 && self
89 .object_type()?
90 .is_none_or(|value| value.trim().is_empty())
91 {
92 return Err(StructuralError::SemanticViolation {
93 entity: Some(self.record.id),
94 rule: "USERDEFINED analysis model requires ObjectType",
95 });
96 }
97 Ok(parsed)
98 }
99
100 pub fn orientation_of_2d_plane(&self) -> StructuralResult<Option<EntityId>> {
102 self.record
103 .optional_ref("OrientationOf2DPlane", "IfcAxis2Placement3D")
104 }
105
106 pub fn loaded_by(&self) -> StructuralResult<Vec<EntityId>> {
108 self.record
109 .optional_set_refs("LoadedBy", "IfcStructuralLoadGroup", 1)
110 }
111
112 pub fn result_groups(&self) -> StructuralResult<Vec<EntityId>> {
114 self.record
115 .optional_set_refs("HasResults", "IfcStructuralResultGroup", 1)
116 }
117
118 pub fn shared_placement(&self) -> StructuralResult<Option<EntityId>> {
121 if !self.record.has_attribute("SharedPlacement") {
122 return Ok(None);
123 }
124 self.record
125 .optional_ref("SharedPlacement", "IfcObjectPlacement")
126 }
127}