1use axiolid_model::NodeId;
12use ifc_model::EntityId;
13
14use crate::error::GeometryResult;
15use crate::lower::bbox::lower_bounding_box_node;
16use crate::lower::boolean::lower_boolean_result_node;
17use crate::lower::brep::{lower_face_surface_node, lower_faceted_brep_node};
18use crate::lower::collection::lower_collection_node;
19use crate::lower::csg::{
20 lower_csg_primitive_node, lower_csg_solid_node, lower_surface_curve_swept_area_solid_node,
21 lower_swept_disk_node,
22};
23use crate::lower::curve::lower_curve_node;
24use crate::lower::halfspace::lower_half_space_node;
25use crate::lower::mapped::lower_mapped_item_node;
26use crate::lower::point::{lower_point_on_curve_node, lower_point_on_surface_node};
27use crate::lower::session::LoweringSession;
28use crate::lower::surface::lower_surface_node;
29use crate::lower::swept::{
30 lower_extruded_area_solid_node, lower_fixed_reference_sweep_node,
31 lower_revolved_area_solid_node, lower_sectioned_spine_node, lower_tapered_extrusion_node,
32 lower_tapered_revolution_node,
33};
34use crate::lower::tessellated::{lower_polygonal_face_set_node, lower_triangulated_face_set_node};
35use crate::select::is_a;
36use crate::transform::Transform;
37
38pub const IMPLEMENTED: &[&str] = &[
43 "IFCEXTRUDEDAREASOLID",
44 "IFCREVOLVEDAREASOLID",
45 "IFCBOOLEANRESULT",
46 "IFCBOOLEANCLIPPINGRESULT",
47 "IFCMAPPEDITEM",
48 "IFCFACETEDBREP",
49 "IFCFACETEDBREPWITHVOIDS",
50 "IFCADVANCEDBREP",
51 "IFCADVANCEDBREPWITHVOIDS",
52 "IFCHALFSPACESOLID",
53 "IFCBOXEDHALFSPACE",
54 "IFCPOLYGONALBOUNDEDHALFSPACE",
55 "IFCTRIANGULATEDFACESET",
56 "IFCPOLYGONALFACESET",
57 "IFCCSGSOLID",
58 "IFCSWEPTDISKSOLID",
59 "IFCSWEPTDISKSOLIDPOLYGONAL",
60 "IFCSURFACECURVESWEPTAREASOLID",
61 "IFCBLOCK",
62 "IFCSPHERE",
63 "IFCRIGHTCIRCULARCYLINDER",
64 "IFCRIGHTCIRCULARCONE",
65 "IFCRECTANGULARPYRAMID",
66 "IFCBOUNDINGBOX",
67 "IFCEXTRUDEDAREASOLIDTAPERED",
68 "IFCREVOLVEDAREASOLIDTAPERED",
69 "IFCFIXEDREFERENCESWEPTAREASOLID",
70 "IFCSECTIONEDSPINE",
71 "IFCSHELLBASEDSURFACEMODEL",
72 "IFCFACEBASEDSURFACEMODEL",
73 "IFCFACESURFACE",
76 "IFCADVANCEDFACE",
77 "IFCGEOMETRICSET",
78 "IFCGEOMETRICCURVESET",
79 "IFCLINE",
82 "IFCCIRCLE",
83 "IFCELLIPSE",
84 "IFCPOLYLINE",
85 "IFCINDEXEDPOLYCURVE",
86 "IFCCOMPOSITECURVE",
87 "IFCCOMPOSITECURVEONSURFACE",
88 "IFCBOUNDARYCURVE",
89 "IFCOUTERBOUNDARYCURVE",
90 "IFCTRIMMEDCURVE",
91 "IFCOFFSETCURVE2D",
92 "IFCOFFSETCURVE3D",
93 "IFCPCURVE",
94 "IFCSURFACECURVE",
95 "IFCINTERSECTIONCURVE",
96 "IFCSEAMCURVE",
97 "IFCBSPLINECURVEWITHKNOTS",
98 "IFCRATIONALBSPLINECURVEWITHKNOTS",
99 "IFCPLANE",
100 "IFCCYLINDRICALSURFACE",
101 "IFCSPHERICALSURFACE",
102 "IFCTOROIDALSURFACE",
103 "IFCSURFACEOFLINEAREXTRUSION",
104 "IFCSURFACEOFREVOLUTION",
105 "IFCRECTANGULARTRIMMEDSURFACE",
106 "IFCCURVEBOUNDEDPLANE",
107 "IFCCURVEBOUNDEDSURFACE",
108 "IFCBSPLINESURFACEWITHKNOTS",
109 "IFCRATIONALBSPLINESURFACEWITHKNOTS",
110 "IFCPOINTONCURVE",
111 "IFCPOINTONSURFACE",
112];
113
114pub const PLANNED: &[(&str, &str)] = &[];
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
131pub struct Variant {
132 pub family: &'static str,
134 pub variant: &'static str,
136 pub support: Support,
138 pub rationale: &'static str,
140}
141
142#[derive(Debug, Clone, Copy, PartialEq, Eq)]
144pub enum Support {
145 Admitted,
147 Refused,
149}
150
151pub const PARTIAL: &[Variant] = &[
158 Variant {
159 family: "IFCPCURVE",
160 variant: "reference curve is an IfcPolyline",
161 support: Support::Admitted,
162 rationale: "an ordered 2D point sequence needs no evaluation",
163 },
164 Variant {
165 family: "IFCPCURVE",
166 variant: "reference curve is an IfcIndexedPolyCurve with no explicit \
167 Segments, or only IfcLineIndex segments",
168 support: Support::Admitted,
169 rationale: "reads identically to a plain ordered point sequence",
170 },
171 Variant {
172 family: "IFCPCURVE",
173 variant: "reference curve is an IfcLine, IfcCircle or IfcEllipse \
174 positioned by an IfcAxis2Placement2D",
175 support: Support::Admitted,
176 rationale: "defining values are read verbatim in the surface's own \
177 (u, v) domain with no unit conversion",
178 },
179 Variant {
180 family: "IFCPCURVE",
181 variant: "reference conic positioned by an IfcAxis2Placement3D",
182 support: Support::Refused,
183 rationale: "a 3D placement's axis has no meaning in a 2D parameter \
184 domain; admitting it would require inventing a projection",
185 },
186 Variant {
187 family: "IFCPCURVE",
188 variant: "reference curve is an IfcIndexedPolyCurve with an explicit \
189 IfcArcIndex segment",
190 support: Support::Admitted,
191 rationale: "a three-point arc composes exactly from a parameter-space \
192 circumcentre into Circle2 plus a Cartesian trim, mirroring \
193 the 3D path with no approximation",
194 },
195 Variant {
196 family: "IFCPCURVE",
197 variant: "reference curve is an explicit-knot IfcBSplineCurveWithKnots \
198 or IfcRationalBSplineCurveWithKnots",
199 support: Support::Admitted,
200 rationale: "every field is dimensionless or a curve parameter; knots \
201 already pass through the 3D path unscaled, and control \
202 points are read as raw (u, v) pairs",
203 },
204 Variant {
205 family: "IFCPCURVE",
206 variant: "reference curve is a trimmed or composite curve",
207 support: Support::Admitted,
208 rationale: "trim parameters and segments stay in the surface (u, v) \
209 domain, unscaled, so no dimensional contract is needed",
210 },
211 Variant {
212 family: "IFCPCURVE",
213 variant: "reference curve is a convention-only IfcBSplineCurve",
214 support: Support::Refused,
215 rationale: "a base spline carries no authored knot vector to preserve",
216 },
217 Variant {
218 family: "IFCSURFACECURVE",
219 variant: "MasterRepresentation is Curve3D, PCurveS1, or PCurveS2 with \
220 the named side present",
221 support: Support::Admitted,
222 rationale: "each side pairs a surface with its own p-curve, so the \
223 neutral MasterRepresentation names S1 and S2 exactly",
224 },
225 Variant {
226 family: "IFCSURFACECURVE",
227 variant: "MasterRepresentation is PCurveS2 with only one associated \
228 p-curve",
229 support: Support::Refused,
230 rationale: "the master names a parametric side the curve does not \
231 have; the schema calls this inconsistent, so it is \
232 refused rather than resolved to the remaining p-curve",
233 },
234];
235
236pub fn lower_representation_item(
241 session: &mut LoweringSession<'_>,
242 id: EntityId,
243 frame: Transform,
244) -> GeometryResult<NodeId> {
245 let type_name = session.type_name(id)?;
246 if is_a(&type_name, "IFCCURVE") {
251 return lower_curve_node(session, id, frame);
252 }
253 if is_a(&type_name, "IFCSURFACE") {
254 return lower_surface_node(session, id, frame);
255 }
256 match type_name.as_str() {
257 "IFCEXTRUDEDAREASOLID" => lower_extruded_area_solid_node(session, id, frame),
258 "IFCREVOLVEDAREASOLID" => lower_revolved_area_solid_node(session, id, frame),
259 "IFCBOOLEANRESULT" | "IFCBOOLEANCLIPPINGRESULT" => {
260 lower_boolean_result_node(session, id, frame)
261 }
262 "IFCHALFSPACESOLID" | "IFCBOXEDHALFSPACE" | "IFCPOLYGONALBOUNDEDHALFSPACE" => {
263 lower_half_space_node(session, id, frame)
264 }
265 "IFCMAPPEDITEM" => lower_mapped_item_node(session, id, frame),
266 "IFCPOINTONCURVE" => lower_point_on_curve_node(session, id, frame),
267 "IFCPOINTONSURFACE" => lower_point_on_surface_node(session, id, frame),
268 "IFCFACETEDBREP"
269 | "IFCFACETEDBREPWITHVOIDS"
270 | "IFCADVANCEDBREP"
271 | "IFCADVANCEDBREPWITHVOIDS" => lower_faceted_brep_node(session, id, frame),
272 "IFCFACESURFACE" | "IFCADVANCEDFACE" => lower_face_surface_node(session, id, frame),
273 "IFCTRIANGULATEDFACESET" => lower_triangulated_face_set_node(session, id, frame),
274 "IFCPOLYGONALFACESET" => lower_polygonal_face_set_node(session, id, frame),
275 "IFCCSGSOLID" => lower_csg_solid_node(session, id, frame),
276 "IFCSWEPTDISKSOLID" | "IFCSWEPTDISKSOLIDPOLYGONAL" => {
277 lower_swept_disk_node(session, id, frame)
278 }
279 "IFCSURFACECURVESWEPTAREASOLID" => {
280 lower_surface_curve_swept_area_solid_node(session, id, frame)
281 }
282 "IFCBOUNDINGBOX" => lower_bounding_box_node(session, id, frame),
283 "IFCEXTRUDEDAREASOLIDTAPERED" => lower_tapered_extrusion_node(session, id, frame),
284 "IFCREVOLVEDAREASOLIDTAPERED" => lower_tapered_revolution_node(session, id, frame),
285 "IFCFIXEDREFERENCESWEPTAREASOLID" => lower_fixed_reference_sweep_node(session, id, frame),
286 "IFCSECTIONEDSPINE" => lower_sectioned_spine_node(session, id, frame),
287 "IFCSHELLBASEDSURFACEMODEL"
288 | "IFCFACEBASEDSURFACEMODEL"
289 | "IFCGEOMETRICSET"
290 | "IFCGEOMETRICCURVESET" => lower_collection_node(session, id, frame),
291 "IFCBLOCK"
292 | "IFCSPHERE"
293 | "IFCRIGHTCIRCULARCYLINDER"
294 | "IFCRIGHTCIRCULARCONE"
295 | "IFCRECTANGULARPYRAMID" => lower_csg_primitive_node(session, id, frame),
296 other => Err(session.unsupported(id, other, detail_for(other))),
297 }
298}
299
300fn detail_for(type_name: &str) -> &'static str {
302 PLANNED
303 .iter()
304 .find(|(name, _)| *name == type_name)
305 .map(|(_, detail)| *detail)
306 .unwrap_or("representation item family is not lowered yet")
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312
313 #[test]
314 fn implemented_and_planned_families_do_not_overlap() {
315 for name in IMPLEMENTED {
316 assert!(
317 !PLANNED.iter().any(|(planned, _)| planned == name),
318 "{name} is listed as both implemented and planned"
319 );
320 }
321 }
322
323 #[test]
324 fn every_planned_family_states_a_concrete_reason() {
325 for (name, detail) in PLANNED {
326 assert!(!detail.is_empty(), "{name} has no stated reason");
327 assert_ne!(
328 *detail, "unsupported",
329 "{name} must say what specifically is missing"
330 );
331 }
332 }
333}