#![allow(clippy::unwrap_used)]
#![allow(clippy::default_trait_access)]
#![allow(unused_imports)]
use std::fmt::Write as _;
use std::io::Cursor;
use cadmpeg_core::decode::{DecodeMode, InspectOptions};
use cadmpeg_ir::codec::{Codec, CodecBackend, Confidence, DecodeOptions};
use cadmpeg_ir::eval::{
model_curve_point_by_id, model_surface_partials_by_id, model_surface_point_by_id, pcurve_uv,
};
use cadmpeg_ir::examples::unit_cube;
use cadmpeg_ir::geometry::{
Curve, CurveGeometry, NurbsCurve, NurbsSurface, PcurveGeometry, Surface, SurfaceGeometry,
};
use cadmpeg_ir::ids::{CurveId, ProceduralCurveId, SurfaceId};
use cadmpeg_ir::index::ModelIndex;
use cadmpeg_ir::math::{Point2, Point3, Vector3};
use cadmpeg_ir::transform::Transform;
use cadmpeg_ir::units::{LengthUnit, Units};
use cadmpeg_ir::CadIr;
use zip::write::SimpleFileOptions;
use zip::{CompressionMethod, ZipWriter};
use crate::ids::StepIdentity;
use crate::test_support::{decode_inline, export};
use crate::{
write_step, StepCodec, StepError, StepSchema, StepUnsupportedPolicy, StepWriteOptions,
};
#[test]
pub(crate) fn decode_and_write_singular_vertex_loops() {
let bytes = include_bytes!("../../../../tests/fixtures/ap242_vertex_loop.p21");
let result = StepCodec::default()
.decode(&mut Cursor::new(bytes), &DecodeOptions::default())
.expect("decode vertex loops");
assert_eq!(result.ir().model.loops.len(), 2);
assert!(result
.ir()
.model
.loops
.iter()
.all(|loop_| loop_.coedges.is_empty() && loop_.vertex_uses.len() == 1));
let validation = cadmpeg_ir::validate_neutral(result.ir(), result.report().losses.clone());
assert!(validation.is_ok(), "{:#?}", validation.findings);
let mut encoded = Vec::new();
write_step(result.ir(), &mut encoded, &StepWriteOptions::default())
.expect("write vertex loops");
assert_eq!(
String::from_utf8(encoded)
.unwrap()
.matches("VERTEX_LOOP")
.count(),
2
);
}
#[test]
pub(crate) fn decode_builds_a_valid_connected_sheet_brep() {
use cadmpeg_ir::topology::{BodyKind, Sense};
let bytes = include_bytes!("../../../../tests/fixtures/ap214_sheet.p21");
let result = StepCodec::default()
.decode(&mut Cursor::new(bytes), &DecodeOptions::default())
.expect("decode AP214 sheet");
assert_eq!(result.ir().model.bodies.len(), 1);
assert_eq!(result.ir().model.bodies[0].kind, BodyKind::Sheet);
assert_eq!(result.ir().model.regions.len(), 1);
assert_eq!(result.ir().model.shells.len(), 1);
assert_eq!(result.ir().model.faces.len(), 1);
assert_eq!(result.ir().model.loops.len(), 1);
assert_eq!(result.ir().model.coedges.len(), 3);
assert_eq!(result.ir().model.edges.len(), 3);
assert!(result.ir().model.edges.iter().all(|edge| {
edge.param_range
.is_some_and(|[start, end]| start.is_finite() && end.is_finite() && start < end)
}));
assert_eq!(result.ir().model.vertices.len(), 3);
assert_eq!(result.ir().model.pcurves.len(), 1);
assert_eq!(
result
.ir()
.model
.coedges
.iter()
.filter(|coedge| !coedge.pcurves.is_empty())
.count(),
1
);
assert!(matches!(
result.ir().model.pcurves[0].geometry,
cadmpeg_ir::geometry::PcurveGeometry::Line { origin, direction }
if origin == cadmpeg_ir::math::Point2::new(0.0, 0.0)
&& direction == cadmpeg_ir::math::Point2::new(1.0, 0.0)
));
assert!(result
.ir()
.model
.coedges
.iter()
.all(|coedge| coedge.sense == Sense::Forward));
assert_eq!(result.ir().model.faces[0].sense, Sense::Reversed);
assert!(result
.ir()
.model
.appearance_bindings
.iter()
.any(|binding| matches!(
binding.target,
cadmpeg_ir::appearance::AppearanceTarget::Edge(_)
)));
assert_eq!(
result.ir().model.faces[0].color,
Some(cadmpeg_ir::topology::Color {
r: 0.9,
g: 0.1,
b: 0.1,
a: 1.0,
})
);
assert_eq!(result.ir().model.presentation_layers.len(), 1);
assert_eq!(
result.ir().model.presentation_layers[0].name,
"machined faces"
);
assert!(matches!(
result.ir().model.presentation_layers[0].items.as_slice(),
[cadmpeg_ir::PresentationItem::Face { .. }]
));
let validation = cadmpeg_ir::validate_neutral(result.ir(), result.report().losses.clone());
assert!(validation.is_ok(), "{:#?}", validation.findings);
let mut output = Vec::new();
let report = write_step(result.ir(), &mut output, &StepWriteOptions::default())
.expect("write sheet pcurve");
assert!(!report
.losses
.iter()
.any(|loss| loss.message.contains("coedge pcurve(s) use unsupported")));
let roundtrip = StepCodec::default()
.decode(&mut Cursor::new(output), &DecodeOptions::default())
.expect("decode written pcurve");
assert_eq!(roundtrip.ir().model.pcurves.len(), 1);
assert_eq!(roundtrip.ir().model.bodies[0].kind, BodyKind::Sheet);
assert_eq!(roundtrip.ir().model.presentation_layers.len(), 1);
assert_eq!(
roundtrip.ir().model.presentation_layers[0].name,
"machined faces"
);
assert!(roundtrip
.ir()
.model
.appearance_bindings
.iter()
.any(|binding| matches!(
binding.target,
cadmpeg_ir::appearance::AppearanceTarget::Edge(_)
)));
assert_eq!(
roundtrip
.ir()
.model
.coedges
.iter()
.filter(|coedge| !coedge.pcurves.is_empty())
.count(),
1
);
}
#[test]
pub(crate) fn decode_builds_a_valid_ap203_sheet_brep() {
use cadmpeg_ir::topology::BodyKind;
let bytes = include_bytes!("../../../../tests/fixtures/ap203_sheet.p21");
let result = StepCodec::default()
.decode(&mut Cursor::new(bytes), &DecodeOptions::default())
.expect("decode AP203 sheet");
assert_eq!(
result.ir().source.as_ref().unwrap().attributes["schema"],
"CONFIG_CONTROL_DESIGN"
);
assert_eq!(result.ir().model.bodies.len(), 1);
assert_eq!(result.ir().model.bodies[0].kind, BodyKind::Sheet);
assert_eq!(result.ir().model.faces.len(), 1);
assert_eq!(result.ir().model.edges.len(), 3);
assert_eq!(result.ir().model.vertices.len(), 3);
let composite = result
.ir()
.model
.curves
.iter()
.find(|curve| curve.id.as_str() == "step:data:curve#34")
.expect("outer composite curve");
assert!(matches!(
&composite.geometry,
cadmpeg_ir::geometry::CurveGeometry::Composite {
segments,
self_intersect: Some(false)
} if segments.len() == 1
&& segments[0].curve.as_str() == "step:data:curve#36"
&& segments[0].same_sense
&& segments[0].transition
== cadmpeg_ir::geometry::CompositeCurveTransition::ContSameGradient
));
assert!(result
.ir()
.model
.procedural_surfaces
.iter()
.any(|surface| matches!(
&surface.definition,
cadmpeg_ir::geometry::ProceduralSurfaceDefinition::CurveBounded {
support,
boundaries,
implicit_outer: false,
..
} if support.as_str() == "step:data:surface#28"
&& boundaries.as_slice() == [cadmpeg_ir::ids::CurveId("step:data:curve#34".into())]
)));
let validation = cadmpeg_ir::validate_neutral(result.ir(), result.report().losses.clone());
assert!(validation.is_ok(), "{:#?}", validation.findings);
let mut encoded = Vec::new();
write_step(result.ir(), &mut encoded, &StepWriteOptions::default())
.expect("write composite curve graph");
let roundtrip = StepCodec::default()
.decode(&mut Cursor::new(encoded), &DecodeOptions::default())
.expect("decode written composite curve graph");
assert!(roundtrip
.ir()
.model
.curves
.iter()
.any(|curve| matches!(curve.geometry, CurveGeometry::Composite { .. })));
}
#[test]
fn decode_builds_a_face_based_surface_model() {
use cadmpeg_ir::topology::BodyKind;
let source =
String::from_utf8(include_bytes!("../../../../tests/fixtures/ap214_sheet.p21").to_vec())
.expect("fixture is UTF-8")
.replace(
"#31=SHELL_BASED_SURFACE_MODEL('',(#33));",
"#31=FACE_BASED_SURFACE_MODEL('',(#30));",
)
.replace(
"#30=OPEN_SHELL('',(#29));",
"#30=CONNECTED_FACE_SET('',(#29));",
);
let result = StepCodec::default()
.decode(&mut Cursor::new(source), &DecodeOptions::default())
.expect("decode face-based surface model");
assert_eq!(
result.ir().model.bodies.len(),
1,
"{:#?}",
result.report().losses
);
assert_eq!(result.ir().model.bodies[0].kind, BodyKind::Sheet);
assert_eq!(result.ir().model.faces.len(), 1);
assert!(result
.report()
.losses
.iter()
.all(|loss| !loss.message.contains("does not resolve to a complete")));
let validation = cadmpeg_ir::validate_neutral(result.ir(), result.report().losses.clone());
assert!(validation.is_ok(), "{:#?}", validation.findings);
}
#[test]
fn decode_builds_faceted_brep_polygon_loops() {
use cadmpeg_ir::topology::BodyKind;
let source =
String::from_utf8(include_bytes!("../../../../tests/fixtures/ap214_sheet.p21").to_vec())
.expect("fixture is UTF-8")
.replace(
"#25=EDGE_LOOP('',(#22,#23,#24));",
"#25=POLY_LOOP('',(#3,#4,#5,#3));",
)
.replace("#30=OPEN_SHELL('',(#29));", "#30=CLOSED_SHELL('',(#29));")
.replace(
"#31=SHELL_BASED_SURFACE_MODEL('',(#33));",
"#31=FACETED_BREP('',#30);",
);
let result = StepCodec::default()
.decode(&mut Cursor::new(source), &DecodeOptions::default())
.expect("decode faceted brep");
assert_eq!(
result.ir().model.bodies.len(),
1,
"{:#?}",
result.report().losses
);
assert_eq!(result.ir().model.bodies[0].kind, BodyKind::Solid);
assert_eq!(result.ir().model.faces.len(), 1);
assert_eq!(result.ir().model.coedges.len(), 3);
assert!(result
.ir()
.model
.edges
.iter()
.all(|edge| edge.curve.is_none()));
let validation = cadmpeg_ir::validate_neutral(result.ir(), result.report().losses.clone());
assert!(validation.is_ok(), "{:#?}", validation.findings);
}
#[test]
fn sheet_root_salvages_independent_shells() {
let source = String::from_utf8(include_bytes!("../../../../tests/fixtures/ap214_sheet.p21").to_vec())
.expect("fixture is UTF-8")
.replace(
"#31=SHELL_BASED_SURFACE_MODEL('',(#33));",
"#31=SHELL_BASED_SURFACE_MODEL('',(#33,#34));",
)
.replace(
"#33=ORIENTED_OPEN_SHELL('',*,#30,.F.);",
"#33=ORIENTED_OPEN_SHELL('',*,#30,.F.);\n#34=ORIENTED_OPEN_SHELL('',*,#99,.T.);\n#99=UNSUPPORTED_SHELL('',());",
);
let decoded = StepCodec::default()
.decode(&mut Cursor::new(source), &DecodeOptions::default())
.expect("decode sheet with one invalid shell");
assert_eq!(
decoded.ir().model.bodies.len(),
1,
"{:#?}",
decoded.report().losses
);
assert!(decoded
.report()
.losses
.iter()
.any(|loss| loss.message.contains("omitted 1 unresolved shell")));
assert!(decoded
.report()
.losses
.iter()
.any(|loss| loss.message.contains("shell carrier #34")));
let validation = cadmpeg_ir::validate_neutral(decoded.ir(), decoded.report().losses.clone());
assert!(validation.is_ok(), "{:#?}", validation.findings);
}
#[test]
fn decode_builds_a_sheet_from_a_geometric_surface_set() {
use cadmpeg_ir::topology::BodyKind;
let bytes = include_bytes!("../../../../tests/fixtures/ap242_geometric_set.p21");
let result = StepCodec::default()
.decode(&mut Cursor::new(bytes), &DecodeOptions::default())
.expect("decode geometric surface set");
assert_eq!(result.ir().model.bodies.len(), 1);
assert_eq!(result.ir().model.bodies[0].kind, BodyKind::Sheet);
assert_eq!(result.ir().model.faces.len(), 1);
assert!(result.ir().model.faces[0].loops.is_empty());
assert_eq!(
result.ir().model.faces[0].surface.as_str(),
"step:data:surface#11"
);
let validation = cadmpeg_ir::validate_neutral(result.ir(), result.report().losses.clone());
assert!(validation.is_ok(), "{:#?}", validation.findings);
}
#[test]
fn complex_geometric_set_representation_uses_its_named_items() {
use cadmpeg_ir::topology::BodyKind;
let mut source = String::from_utf8(include_bytes!(
"../../../../tests/fixtures/ap242_geometric_set.p21"
)
.to_vec())
.expect("fixture is UTF-8")
.replace(
"#12=GEOMETRIC_SET('',(#11));",
"#12=GEOMETRIC_SET('',(#11,#14,#15));",
)
.replace(
"#13=GEOMETRICALLY_BOUNDED_SURFACE_SHAPE_REPRESENTATION('',(#12),#2);",
"#13=(GEOMETRICALLY_BOUNDED_SURFACE_SHAPE_REPRESENTATION() REPRESENTATION('',(#12),#2) SHAPE_REPRESENTATION());",
);
let end = source.rfind("ENDSEC;").expect("STEP data section end");
source.insert_str(
end,
"#14=(CIRCLE('',#6,5.) CURVE() GEOMETRIC_REPRESENTATION_ITEM() REPRESENTATION_ITEM('free circle'));\n#15=UNSUPPORTED_ITEM('');\n",
);
let result = StepCodec::default()
.decode(&mut Cursor::new(source), &DecodeOptions::default())
.expect("decode complex geometric surface set");
assert_eq!(result.ir().model.bodies.len(), 1);
assert_eq!(result.ir().model.bodies[0].kind, BodyKind::Sheet);
assert_eq!(result.ir().model.faces.len(), 1);
let free_circle = result
.ir()
.model
.curves
.iter()
.find(|curve| curve.id.as_str() == "step:data:curve#14")
.expect("free complex representation member");
assert_eq!(
free_circle
.source_object
.as_ref()
.and_then(|source| source.name.as_deref()),
Some("free circle")
);
assert!(result.report().losses.iter().any(|loss| {
loss.message.contains(
"GEOMETRICALLY_BOUNDED_SURFACE_SHAPE_REPRESENTATION #13 omitted unsupported or unresolved member(s): #15",
)
}));
let validation = cadmpeg_ir::validate_neutral(result.ir(), result.report().losses.clone());
assert!(validation.is_ok(), "{:#?}", validation.findings);
}
#[test]
pub(crate) fn reader_recovers_a_valid_solid_from_writer_output() {
use cadmpeg_ir::topology::BodyKind;
let source = unit_cube();
let mut bytes = Vec::new();
write_step(&source, &mut bytes, &StepWriteOptions::default()).unwrap();
let result = StepCodec::default()
.decode(&mut Cursor::new(bytes), &DecodeOptions::default())
.expect("decode generated cube STEP");
assert_eq!(result.ir().model.bodies.len(), 1);
assert_eq!(result.ir().model.bodies[0].kind, BodyKind::Solid);
assert_eq!(result.ir().model.faces.len(), 6);
assert_eq!(result.ir().model.edges.len(), 12);
assert_eq!(result.ir().model.vertices.len(), 8);
let validation = cadmpeg_ir::validate_neutral(result.ir(), result.report().losses.clone());
assert!(validation.is_ok(), "{:#?}", validation.findings);
}