ifc_lite_geometry/router/transforms/
mapped.rs1use super::super::GeometryRouter;
9use crate::{Error, Result};
10use ifc_lite_core::{DecodedEntity, EntityDecoder, IfcType};
11use nalgebra::Matrix4;
12
13impl GeometryRouter {
14 pub fn resolve_scaled_mapped_item_transform(
18 &self,
19 item: &DecodedEntity,
20 source: &DecodedEntity,
21 decoder: &mut EntityDecoder,
22 ) -> Result<Option<[f64; 16]>> {
23 self.resolve_scaled_mapped_item_transform_with_origin_loader(item, decoder,
24 |decoder| self.mapping_origin_transform(source, decoder))
25 }
26
27 pub fn resolve_scaled_mapped_item_transform_with_origin_loader<F>(
31 &self,
32 item: &DecodedEntity,
33 decoder: &mut EntityDecoder,
34 origin: F,
35 ) -> Result<Option<[f64; 16]>>
36 where F: FnOnce(&mut EntityDecoder) -> Result<Option<Matrix4<f64>>> {
37 let target = self.mapping_target_transform(item, decoder)?;
38 let origin = origin(decoder)?;
39 let Some(mut matrix) = Self::compose_mapped_transform(target, origin) else {
40 return Ok(None);
41 };
42 self.scale_transform(&mut matrix);
43 Ok(Some(std::array::from_fn(|i| matrix.as_slice()[i])))
44 }
45
46 pub(crate) fn mapped_item_transform(
62 &self,
63 item: &DecodedEntity,
64 source: &DecodedEntity,
65 decoder: &mut EntityDecoder,
66 ) -> Result<Option<Matrix4<f64>>> {
67 let target = self.mapping_target_transform(item, decoder)?;
68 let origin = self.mapping_origin_transform(source, decoder)?;
69 Ok(Self::compose_mapped_transform(target, origin))
70 }
71
72 fn mapping_target_transform(
73 &self, item: &DecodedEntity, decoder: &mut EntityDecoder,
74 ) -> Result<Option<Matrix4<f64>>> {
75 match item.get(1) {
76 Some(attr) if !attr.is_null() => match decoder.resolve_ref(attr)? {
77 Some(entity) => Ok(Some(self.parse_cartesian_transformation_operator(&entity, decoder)?)),
78 None => Ok(None),
79 },
80 _ => Ok(None),
81 }
82 }
83
84 fn compose_mapped_transform(
85 target: Option<Matrix4<f64>>, origin: Option<Matrix4<f64>>,
86 ) -> Option<Matrix4<f64>> {
87 match (target, origin) {
88 (Some(t), Some(o)) => Some(t * o),
89 (Some(t), None) => Some(t),
90 (None, Some(o)) => Some(o),
91 (None, None) => None,
92 }
93 }
94
95 pub fn mapping_origin_transform(
107 &self,
108 source: &DecodedEntity,
109 decoder: &mut EntityDecoder,
110 ) -> Result<Option<Matrix4<f64>>> {
111 let Some(origin_attr) = source.get(0).filter(|a| !a.is_null()) else {
112 return Ok(None);
113 };
114 let Some(origin) = decoder.resolve_ref(origin_attr)? else {
115 return Err(Error::geometry(
116 "RepresentationMap MappingOrigin does not resolve".to_string(),
117 ));
118 };
119 let m = match origin.ifc_type {
120 IfcType::IfcAxis2Placement3D => self.parse_axis2_placement_3d(&origin, decoder)?,
121 IfcType::IfcAxis2Placement2D => self.parse_axis2_placement_2d(&origin, decoder)?,
123 other => {
124 return Err(Error::geometry(format!(
125 "RepresentationMap MappingOrigin is {other}, not an IfcAxis2Placement"
126 )))
127 }
128 };
129 Ok(if m.is_identity(1e-12) { None } else { Some(m) })
130 }
131
132 #[inline]
135 pub(crate) fn parse_axis2_placement_2d(
136 &self,
137 placement: &DecodedEntity,
138 decoder: &mut EntityDecoder,
139 ) -> Result<Matrix4<f64>> {
140 crate::transform::parse_axis2_placement_2d(placement, decoder)
141 }
142}
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn issue_5786_cached_origin_uses_standard_mapped_composition() {
149 let content = b"#1=IFCCARTESIANPOINT((10.,20.,0.));\n\
150 #2=IFCAXIS2PLACEMENT3D(#1,$,$);\n\
151 #3=IFCREPRESENTATIONMAP(#2,#9);\n\
152 #4=IFCCARTESIANPOINT((100.,0.,0.));\n\
153 #5=IFCCARTESIANTRANSFORMATIONOPERATOR3D($,$,#4,2.,$);\n\
154 #6=IFCMAPPEDITEM(#3,#5);";
155 let router = GeometryRouter::with_scale(0.001);
156 let mut decoder = EntityDecoder::new(content);
157 let item = decoder.decode_by_id(6).unwrap();
158 let source = decoder.decode_by_id(3).unwrap();
159 let standard = router.resolve_scaled_mapped_item_transform(&item, &source, &mut decoder).unwrap();
160 let origin = router.mapping_origin_transform(&source, &mut decoder).unwrap();
161 let cached = router.resolve_scaled_mapped_item_transform_with_origin_loader(
162 &item, &mut decoder, |_| Ok(origin)).unwrap();
163 for transform in [standard, cached] {
166 let matrix = transform.unwrap();
167 assert_eq!(matrix[0], 2.0);
168 assert_eq!(matrix[5], 2.0);
169 assert_eq!(matrix[10], 2.0);
170 assert!((matrix[12] - 0.12).abs() < 1e-12);
171 assert!((matrix[13] - 0.04).abs() < 1e-12);
172 }
173 }
174}