use super::*;
#[test]
fn test_si_prefix_multipliers() {
assert_eq!(get_si_prefix_multiplier("MILLI"), 0.001);
assert_eq!(get_si_prefix_multiplier("CENTI"), 0.01);
assert_eq!(get_si_prefix_multiplier("DECI"), 0.1);
assert_eq!(get_si_prefix_multiplier("KILO"), 1000.0);
assert_eq!(get_si_prefix_multiplier(""), 1.0);
assert_eq!(get_si_prefix_multiplier("UNKNOWN"), 1.0);
}
#[test]
fn test_extract_unit_from_real_file() {
let ifc_content = r#"ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('Test'),'2;1');
FILE_NAME('test.ifc','2024-01-01',(''),(''),'','','');
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROJECT('guid',$,'Test',$,$,$,$,(#2),#3);
#2=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-5,#4,$);
#3=IFCUNITASSIGNMENT((#5));
#4=IFCAXIS2PLACEMENT3D(#6,$,$);
#5=IFCSIUNIT(*,.LENGTHUNIT.,.MILLI.,.METRE.);
#6=IFCCARTESIANPOINT((0.,0.,0.));
ENDSEC;
END-ISO-10303-21;
"#;
let mut decoder = EntityDecoder::new(ifc_content);
let scale = extract_length_unit_scale(&mut decoder, 1).unwrap();
assert!(
(scale - 0.001).abs() < 0.0001,
"Expected 0.001 for MILLI, got {}",
scale
);
}
#[test]
fn test_extract_unit_meters() {
let ifc_content = r#"ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('Test'),'2;1');
FILE_NAME('test.ifc','2024-01-01',(''),(''),'','','');
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROJECT('guid',$,'Test',$,$,$,$,(#2),#3);
#2=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-5,#4,$);
#3=IFCUNITASSIGNMENT((#5));
#4=IFCAXIS2PLACEMENT3D(#6,$,$);
#5=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
#6=IFCCARTESIANPOINT((0.,0.,0.));
ENDSEC;
END-ISO-10303-21;
"#;
let mut decoder = EntityDecoder::new(ifc_content);
let scale = extract_length_unit_scale(&mut decoder, 1).unwrap();
assert!(
(scale - 1.0).abs() < 0.0001,
"Expected 1.0 for meters, got {}",
scale
);
}
#[test]
fn test_try_extract_length_unit_partial_index_defers() {
use crate::decoder::EntityIndex;
let ifc_content = r#"ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('Test'),'2;1');
FILE_NAME('test.ifc','2024-01-01',(''),(''),'','','');
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#10=IFCEXTRUDEDAREASOLID(#11,#12,#13,3000.);
#100=IFCPROJECT('guid',$,'Test',$,$,$,$,(#2),#200);
#200=IFCUNITASSIGNMENT((#300));
#300=IFCSIUNIT(*,.LENGTHUNIT.,.MILLI.,.METRE.);
ENDSEC;
END-ISO-10303-21;
"#;
let mut partial: EntityIndex = Default::default();
{
let mut scanner = crate::EntityScanner::new(ifc_content);
while let Some((id, type_name, start, end)) = scanner.next_entity() {
partial.insert(id, (start, end));
if type_name == "IFCPROJECT" {
break;
}
}
}
assert!(!partial.contains_key(&200), "test setup: #200 must be absent");
let mut partial_decoder = EntityDecoder::with_index(ifc_content, partial);
assert_eq!(
try_extract_length_unit_scale(&mut partial_decoder, 100),
None,
"partial index must defer, not default to metres"
);
let full = crate::build_entity_index(ifc_content);
let mut full_decoder = EntityDecoder::with_index(ifc_content, full);
assert_eq!(
try_extract_length_unit_scale(&mut full_decoder, 100),
Some(0.001),
"full index must resolve millimetres"
);
assert!(
(extract_length_unit_scale(&mut full_decoder, 100).unwrap() - 0.001).abs() < 1e-9
);
}
#[test]
fn test_try_extract_length_unit_resolves_metres_when_complete() {
let ifc_content = r#"ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('Test'),'2;1');
FILE_NAME('test.ifc','2024-01-01',(''),(''),'','','');
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROJECT('guid',$,'Test',$,$,$,$,(#2),#3);
#3=IFCUNITASSIGNMENT((#5));
#5=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
ENDSEC;
END-ISO-10303-21;
"#;
let mut decoder = EntityDecoder::new(ifc_content);
assert_eq!(try_extract_length_unit_scale(&mut decoder, 1), Some(1.0));
}
#[test]
fn test_conversion_based_unit_factors() {
assert_eq!(get_conversion_based_unit_factor("FOOT"), Some(0.3048));
assert_eq!(get_conversion_based_unit_factor("foot"), Some(0.3048));
assert_eq!(get_conversion_based_unit_factor("FEET"), Some(0.3048));
assert_eq!(get_conversion_based_unit_factor("'FOOT'"), Some(0.3048));
assert_eq!(get_conversion_based_unit_factor("'FEET'"), Some(0.3048));
assert_eq!(get_conversion_based_unit_factor("INCH"), Some(0.0254));
assert_eq!(get_conversion_based_unit_factor("YARD"), Some(0.9144));
assert_eq!(get_conversion_based_unit_factor("MILE"), Some(1609.344));
assert_eq!(get_conversion_based_unit_factor("UNKNOWN_UNIT"), None);
}
#[test]
fn test_extract_plane_angle_radian() {
let ifc_content = r#"ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('Test'),'2;1');
FILE_NAME('test.ifc','2024-01-01',(''),(''),'','','');
FILE_SCHEMA(('IFC2X3'));
ENDSEC;
DATA;
#1=IFCPROJECT('guid',$,'Test',$,$,$,$,(#2),#3);
#2=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-5,#4,$);
#3=IFCUNITASSIGNMENT((#5,#6));
#4=IFCAXIS2PLACEMENT3D(#7,$,$);
#5=IFCSIUNIT(*,.LENGTHUNIT.,.MILLI.,.METRE.);
#6=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
#7=IFCCARTESIANPOINT((0.,0.,0.));
ENDSEC;
END-ISO-10303-21;
"#;
let mut decoder = EntityDecoder::new(ifc_content);
let scale = extract_plane_angle_to_radians(&mut decoder, 1).unwrap();
assert!(
(scale - 1.0).abs() < 1e-9,
"expected 1.0 for RADIAN, got {}",
scale
);
}
#[test]
fn test_extract_plane_angle_degree() {
let ifc_content = r#"ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('Test'),'2;1');
FILE_NAME('test.ifc','2024-01-01',(''),(''),'','','');
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROJECT('guid',$,'Test',$,$,$,$,(#2),#3);
#2=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-5,#4,$);
#3=IFCUNITASSIGNMENT((#5,#10));
#4=IFCAXIS2PLACEMENT3D(#7,$,$);
#5=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
#7=IFCCARTESIANPOINT((0.,0.,0.));
#8=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
#9=IFCMEASUREWITHUNIT(IFCRATIOMEASURE(0.0174532925199433),#8);
#10=IFCCONVERSIONBASEDUNIT(#11,.PLANEANGLEUNIT.,'DEGREE',#9);
#11=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0);
ENDSEC;
END-ISO-10303-21;
"#;
let mut decoder = EntityDecoder::new(ifc_content);
let scale = extract_plane_angle_to_radians(&mut decoder, 1).unwrap();
assert!(
(scale - 0.0174532925199433).abs() < 1e-9,
"expected 0.01745… for DEGREE, got {}",
scale
);
}
#[test]
fn test_extract_plane_angle_missing_defaults_to_radian() {
let ifc_content = r#"ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('Test'),'2;1');
FILE_NAME('test.ifc','2024-01-01',(''),(''),'','','');
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROJECT('guid',$,'Test',$,$,$,$,(#2),#3);
#2=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-5,#4,$);
#3=IFCUNITASSIGNMENT((#5));
#4=IFCAXIS2PLACEMENT3D(#6,$,$);
#5=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
#6=IFCCARTESIANPOINT((0.,0.,0.));
ENDSEC;
END-ISO-10303-21;
"#;
let mut decoder = EntityDecoder::new(ifc_content);
let scale = extract_plane_angle_to_radians(&mut decoder, 1).unwrap();
assert!(
(scale - 1.0).abs() < 1e-9,
"expected 1.0 default for missing PLANEANGLEUNIT, got {}",
scale
);
}
#[test]
fn test_try_extract_plane_angle_degree_full_index() {
let ifc_content = r#"ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('Test'),'2;1');
FILE_NAME('test.ifc','2024-01-01',(''),(''),'','','');
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROJECT('guid',$,'Test',$,$,$,$,(#2),#3);
#2=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-5,#4,$);
#3=IFCUNITASSIGNMENT((#5,#10));
#4=IFCAXIS2PLACEMENT3D(#7,$,$);
#5=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
#7=IFCCARTESIANPOINT((0.,0.,0.));
#8=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
#9=IFCMEASUREWITHUNIT(IFCRATIOMEASURE(0.0174532925199433),#8);
#10=IFCCONVERSIONBASEDUNIT(#11,.PLANEANGLEUNIT.,'DEGREE',#9);
#11=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0);
ENDSEC;
END-ISO-10303-21;
"#;
let mut decoder = EntityDecoder::new(ifc_content);
let scale = try_extract_plane_angle_to_radians(&mut decoder, 1).unwrap();
assert!((scale - 0.0174532925199433).abs() < 1e-9, "got {}", scale);
}
#[test]
fn test_try_extract_plane_angle_incomplete_chain_returns_none() {
let ifc_content = r#"ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('Test'),'2;1');
FILE_NAME('test.ifc','2024-01-01',(''),(''),'','','');
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROJECT('guid',$,'Test',$,$,$,$,(#2),#3);
#2=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-5,#4,$);
#3=IFCUNITASSIGNMENT((#5,#10));
#4=IFCAXIS2PLACEMENT3D(#7,$,$);
#5=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
#7=IFCCARTESIANPOINT((0.,0.,0.));
#8=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
#9=IFCMEASUREWITHUNIT(IFCRATIOMEASURE(0.0174532925199433),#8);
#10=IFCCONVERSIONBASEDUNIT(#11,.PLANEANGLEUNIT.,'DEGREE',#9);
#11=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0);
ENDSEC;
END-ISO-10303-21;
"#;
let full = crate::decoder::build_entity_index(&ifc_content);
let partial: crate::decoder::EntityIndex =
full.iter().filter(|(&id, _)| id != 9).map(|(&k, &v)| (k, v)).collect();
let mut decoder = EntityDecoder::with_index(ifc_content, partial);
assert_eq!(try_extract_plane_angle_to_radians(&mut decoder, 1), None);
}
#[test]
fn test_try_extract_plane_angle_radian_and_missing() {
let radian = r#"ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('Test'),'2;1');
FILE_NAME('t.ifc','2024-01-01',(''),(''),'','','');
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROJECT('guid',$,'Test',$,$,$,$,(#2),#3);
#2=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-5,#4,$);
#3=IFCUNITASSIGNMENT((#5,#6));
#4=IFCAXIS2PLACEMENT3D(#7,$,$);
#5=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
#6=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
#7=IFCCARTESIANPOINT((0.,0.,0.));
ENDSEC;
END-ISO-10303-21;
"#;
let mut decoder = EntityDecoder::new(radian);
assert_eq!(try_extract_plane_angle_to_radians(&mut decoder, 1), Some(1.0));
let missing = r#"ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('Test'),'2;1');
FILE_NAME('t.ifc','2024-01-01',(''),(''),'','','');
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROJECT('guid',$,'Test',$,$,$,$,(#2),#3);
#2=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-5,#4,$);
#3=IFCUNITASSIGNMENT((#5));
#4=IFCAXIS2PLACEMENT3D(#6,$,$);
#5=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
#6=IFCCARTESIANPOINT((0.,0.,0.));
ENDSEC;
END-ISO-10303-21;
"#;
let mut decoder = EntityDecoder::new(missing);
assert_eq!(try_extract_plane_angle_to_radians(&mut decoder, 1), Some(1.0));
}
#[test]
fn test_decoder_plane_angle_cache() {
let ifc_content = r#"ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('Test'),'2;1');
FILE_NAME('test.ifc','2024-01-01',(''),(''),'','','');
FILE_SCHEMA(('IFC2X3'));
ENDSEC;
DATA;
#1=IFCPROJECT('guid',$,'Test',$,$,$,$,(#2),#3);
#2=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-5,#4,$);
#3=IFCUNITASSIGNMENT((#5,#6));
#4=IFCAXIS2PLACEMENT3D(#7,$,$);
#5=IFCSIUNIT(*,.LENGTHUNIT.,.MILLI.,.METRE.);
#6=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
#7=IFCCARTESIANPOINT((0.,0.,0.));
ENDSEC;
END-ISO-10303-21;
"#;
let mut decoder = EntityDecoder::new(ifc_content);
let a = decoder.plane_angle_to_radians();
let b = decoder.plane_angle_to_radians();
assert_eq!(a, b);
assert!((a - 1.0).abs() < 1e-9);
}
#[test]
fn test_extract_unit_imperial_feet() {
let ifc_content = r#"ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('Test'),'2;1');
FILE_NAME('test.ifc','2024-01-01',(''),(''),'','','');
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROJECT('guid',$,'Test',$,$,$,$,(#2),#3);
#2=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-5,#4,$);
#3=IFCUNITASSIGNMENT((#5));
#4=IFCAXIS2PLACEMENT3D(#6,$,$);
#5=IFCCONVERSIONBASEDUNIT(#7,.LENGTHUNIT.,'FOOT',#8);
#6=IFCCARTESIANPOINT((0.,0.,0.));
#7=IFCDIMENSIONALEXPONENTS(1,0,0,0,0,0,0);
#8=IFCMEASUREWITHUNIT(IFCLENGTHMEASURE(0.3048),#9);
#9=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
ENDSEC;
END-ISO-10303-21;
"#;
let mut decoder = EntityDecoder::new(ifc_content);
let scale = extract_length_unit_scale(&mut decoder, 1).unwrap();
assert!(
(scale - 0.3048).abs() < 0.0001,
"Expected 0.3048 for FOOT, got {}",
scale
);
}