use runmat_runtime::geometry::{
geometry_load_op, geometry_prep_for_analysis_op, GeometryPrepForAnalysisSpec,
GeometryPrepProfile,
};
use runmat_runtime::operations::OperationContext;
const TRIANGLE_STL: &str = "solid tri\n facet normal 0 0 1\n outer loop\n vertex 0 0 0\n vertex 1 0 0\n vertex 0 1 0\n endloop\n endfacet\nendsolid tri\n";
const SIMPLE_STEP: &str = "ISO-10303-21;\nHEADER;\nFILE_NAME('Assembly_A');\nENDSEC;\nDATA;\n#10=PRODUCT('Bracket_A','',(#1));\nENDSEC;\nEND-ISO-10303-21;\n";
#[test]
fn geometry_prep_for_analysis_is_deterministic_for_fixture_inputs() {
let spec = GeometryPrepForAnalysisSpec {
profile: GeometryPrepProfile::AnalysisReady,
target_element_budget: 120_000,
};
for (path, bytes) in [
("/fixtures/tri.stl", TRIANGLE_STL.as_bytes()),
("/fixtures/assy.step", SIMPLE_STEP.as_bytes()),
] {
let geometry = geometry_load_op(path, bytes, OperationContext::new(None, None))
.expect("geometry load should succeed");
let first = geometry_prep_for_analysis_op(
&geometry.data,
spec.clone(),
OperationContext::new(Some("trace-prep-1".to_string()), None),
)
.expect("first prep should succeed");
let second = geometry_prep_for_analysis_op(
&geometry.data,
spec.clone(),
OperationContext::new(Some("trace-prep-2".to_string()), None),
)
.expect("second prep should succeed");
assert_ne!(first.data.prep_artifact_id, second.data.prep_artifact_id);
assert_eq!(first.data.prep, second.data.prep);
assert!(!first.data.prep_artifact_id.is_empty());
assert_eq!(
first.data.prep.schema_version,
"geometry-prep-for-analysis/v1"
);
assert!(first.data.prep.quality.min_scaled_jacobian >= 0.5);
assert_eq!(first.data.prep.quality.inverted_element_count, 0);
}
}
#[test]
fn geometry_prep_for_analysis_preserves_region_mapping_stability() {
let geometry = geometry_load_op(
"/fixtures/assy.step",
SIMPLE_STEP.as_bytes(),
OperationContext::new(None, None),
)
.expect("geometry load should succeed");
let prep = geometry_prep_for_analysis_op(
&geometry.data,
GeometryPrepForAnalysisSpec::default(),
OperationContext::new(None, None),
)
.expect("prep should succeed");
assert!(!prep.data.prep.region_mappings.is_empty());
for mapping in &prep.data.prep.region_mappings {
assert!(!mapping.region_id.is_empty());
assert!(!mapping.source_mesh_ids.is_empty());
assert!(!mapping.prepared_mesh_ids.is_empty());
}
}
#[test]
fn geometry_prep_for_analysis_adaptive_refine_is_deterministic_and_quality_oriented() {
let geometry = geometry_load_op(
"/fixtures/tri.stl",
TRIANGLE_STL.as_bytes(),
OperationContext::new(None, None),
)
.expect("geometry load should succeed");
let spec = GeometryPrepForAnalysisSpec {
profile: GeometryPrepProfile::AdaptiveRefine,
target_element_budget: 4_000,
};
let first = geometry_prep_for_analysis_op(
&geometry.data,
spec.clone(),
OperationContext::new(Some("trace-prep-adapt-1".to_string()), None),
)
.expect("first adaptive prep should succeed");
let second = geometry_prep_for_analysis_op(
&geometry.data,
spec,
OperationContext::new(Some("trace-prep-adapt-2".to_string()), None),
)
.expect("second adaptive prep should succeed");
assert_eq!(first.data.prep, second.data.prep);
assert!(first.data.prep.quality.min_scaled_jacobian >= 0.85);
assert!(first.data.prep.quality.mean_aspect_ratio <= 2.0);
}