use ifc_lite_processing::process_geometry;
const SOLID_X: f64 = 200_000.0;
const SOLID_Y: f64 = 6_000_000.0;
const SOLID_Z: f64 = 100.0;
fn surface3d_proxy(base: u32, guid: &str) -> String {
let c0 = base;
let c1 = base + 1;
let c2 = base + 2;
let c3 = base + 3;
let bspline = base + 4;
let rep = base + 5;
let pds = base + 6;
let pt = base + 7;
let ax = base + 8;
let plc = base + 9;
let proxy = base + 10;
format!(
"#{c0}=IFCCARTESIANPOINT((0.,0.,0.));\n\
#{c1}=IFCCARTESIANPOINT((1.,0.,0.));\n\
#{c2}=IFCCARTESIANPOINT((0.,1.,0.));\n\
#{c3}=IFCCARTESIANPOINT((1.,1.,0.));\n\
#{bspline}=IFCBSPLINESURFACEWITHKNOTS(1,1,((#{c0},#{c1}),(#{c2},#{c3})),\
.UNSPECIFIED.,.F.,.F.,.F.,(2,2),(2,2),(0.,1.),(0.,1.),.UNSPECIFIED.);\n\
#{rep}=IFCSHAPEREPRESENTATION(#6,'Surface','Surface3D',(#{bspline}));\n\
#{pds}=IFCPRODUCTDEFINITIONSHAPE($,$,(#{rep}));\n\
#{pt}=IFCCARTESIANPOINT((0.,0.,0.));\n\
#{ax}=IFCAXIS2PLACEMENT3D(#{pt},$,$);\n\
#{plc}=IFCLOCALPLACEMENT($,#{ax});\n\
#{proxy}=IFCBUILDINGELEMENTPROXY('{guid}',$,'surf',$,$,#{plc},#{pds},$,$);\n"
)
}
fn model() -> String {
let mut s = String::from(
"ISO-10303-21;\n\
HEADER;\n\
FILE_DESCRIPTION((''),'2;1');\n\
FILE_NAME('','2026-01-01T00:00:00',(''),(''),'t','t','');\n\
FILE_SCHEMA(('IFC4X3_ADD2'));\n\
ENDSEC;\n\
DATA;\n\
#1=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);\n\
#2=IFCUNITASSIGNMENT((#1));\n\
#3=IFCCARTESIANPOINT((0.,0.,0.));\n\
#4=IFCAXIS2PLACEMENT3D(#3,$,$);\n\
#5=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#4,$);\n\
#6=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#5,$,.MODEL_VIEW.,$);\n\
#7=IFCPROJECT('01tEAnIV5BixApwp1YzpwS',$,'t',$,$,$,$,(#5),#2);\n",
);
for (i, guid) in [
"21tEAnIV5BixApwp1YzpwS",
"31tEAnIV5BixApwp1YzpwS",
"41tEAnIV5BixApwp1YzpwS",
"51tEAnIV5BixApwp1YzpwS",
"61tEAnIV5BixApwp1YzpwS",
"71tEAnIV5BixApwp1YzpwS",
]
.iter()
.enumerate()
{
s.push_str(&surface3d_proxy(100 + (i as u32) * 20, guid));
}
s.push_str(&format!(
"#20=IFCCARTESIANPOINTLIST3D((({x},{y},{z}),({x1},{y},{z}),({x},{y1},{z}),({x},{y},{z1})));\n\
#21=IFCTRIANGULATEDFACESET(#20,$,$,((1,2,3),(1,2,4),(1,3,4),(2,3,4)),$);\n\
#22=IFCSHAPEREPRESENTATION(#6,'Body','Tessellation',(#21));\n\
#23=IFCPRODUCTDEFINITIONSHAPE($,$,(#22));\n\
#24=IFCCARTESIANPOINT((0.,0.,0.));\n\
#25=IFCAXIS2PLACEMENT3D(#24,$,$);\n\
#26=IFCLOCALPLACEMENT($,#25);\n\
#27=IFCBUILDINGELEMENTPROXY('11tEAnIV5BixApwp1YzpwS',$,'solid',$,$,#26,#23,$,$);\n",
x = SOLID_X,
y = SOLID_Y,
z = SOLID_Z,
x1 = SOLID_X + 1.0,
y1 = SOLID_Y + 1.0,
z1 = SOLID_Z + 1.0,
));
s.push_str("ENDSEC;\nEND-ISO-10303-21;\n");
s
}
#[test]
fn rtc_offset_survives_surface3d_origin_entities() {
let result = process_geometry(&model());
let shift = result.metadata.coordinate_info.origin_shift;
assert!(
result.metadata.coordinate_info.is_geo_referenced,
"model with a 200 km solid must be flagged geo-referenced despite the Surface3D proxies"
);
assert!(
(shift[0] - SOLID_X).abs() < 10.0,
"RTC X should anchor near the solid ({SOLID_X}), got {} (Surface3D origin votes polluted the median)",
shift[0]
);
assert!(
(shift[1] - SOLID_Y).abs() < 10.0,
"RTC Y should anchor near the solid ({SOLID_Y}), got {} (Surface3D origin votes polluted the median)",
shift[1]
);
}