Another project
1use std::path::PathBuf;
2
3use bone_document::{EditOutcome, Sketch, SketchEdit, SketchEntity};
4use bone_render::{
5 Camera2, PixelDiff, PixelDiffThreshold, SketchRenderer, SketchScene, Style, decode_png,
6 encode_png,
7};
8use bone_types::{Point2, Point3, SketchEntityId, SketchPlaneBasis, Tolerance, UnitVec3};
9
10mod common;
11
12use common::{extent_square as extent, make_context};
13
14const GOLDEN: &str = "tests/goldens/rectangle_256.png";
15const UPDATE_ENV: &str = "BONE_UPDATE_RECTANGLE_GOLDEN";
16const DIFF_TOLERANCE: f64 = 16.0 / 255.0;
17
18fn golden_path() -> PathBuf {
19 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(GOLDEN)
20}
21
22fn plane() -> SketchPlaneBasis {
23 let Ok(basis) = SketchPlaneBasis::new(
24 Point3::origin(),
25 UnitVec3::x_axis(),
26 UnitVec3::y_axis(),
27 Tolerance::new(1e-9),
28 ) else {
29 panic!("xy plane basis is orthogonal");
30 };
31 basis
32}
33
34fn add_point(s: Sketch, x: f64, y: f64) -> (Sketch, SketchEntityId) {
35 let Ok((next, EditOutcome::Entity(id))) = s.apply(SketchEdit::AddEntity(SketchEntity::point(
36 Point2::from_mm(x, y),
37 ))) else {
38 panic!("add point");
39 };
40 (next, id)
41}
42
43fn add_line(s: Sketch, a: SketchEntityId, b: SketchEntityId) -> Sketch {
44 let Ok((next, _)) = s.apply(SketchEdit::AddEntity(SketchEntity::line(a, b, false))) else {
45 panic!("add line");
46 };
47 next
48}
49
50fn rectangle_scene() -> SketchScene {
51 let s = Sketch::new(plane());
52 let (s, p0) = add_point(s, -8.0, -5.0);
53 let (s, p1) = add_point(s, 8.0, -5.0);
54 let (s, p2) = add_point(s, 8.0, 5.0);
55 let (s, p3) = add_point(s, -8.0, 5.0);
56 let s = add_line(s, p0, p1);
57 let s = add_line(s, p1, p2);
58 let s = add_line(s, p2, p3);
59 let s = add_line(s, p3, p0);
60 let Ok(scene) = SketchScene::extract(&s) else {
61 panic!("scene extract");
62 };
63 scene
64}
65
66#[test]
67fn rectangle_sketch_matches_golden() {
68 let size = extent(256);
69 let ctx = make_context(size);
70 let mut renderer = SketchRenderer::new(ctx.gpu(), ctx.color_format());
71 let scene = rectangle_scene();
72 let camera = Camera2::new(size);
73 let style = Style::default();
74
75 let Ok(frame) = renderer.render(&ctx, &scene, camera, &style) else {
76 panic!("SketchRenderer::render failed");
77 };
78
79 let path = golden_path();
80
81 if std::env::var(UPDATE_ENV).is_ok() {
82 let Ok(bytes) = encode_png(&frame) else {
83 panic!("encode_png failed");
84 };
85 if let Some(parent) = path.parent()
86 && let Err(e) = std::fs::create_dir_all(parent)
87 {
88 panic!("create goldens dir {}: {e}", parent.display());
89 }
90 if let Err(e) = std::fs::write(&path, &bytes) {
91 panic!("write golden {}: {e}", path.display());
92 }
93 return;
94 }
95
96 let Ok(bytes) = std::fs::read(&path) else {
97 panic!(
98 "golden missing at {}: rerun with {UPDATE_ENV}=1 to generate",
99 path.display()
100 );
101 };
102 let Ok((golden_extent, golden_rgba)) = decode_png(&bytes) else {
103 panic!("failed to decode golden PNG");
104 };
105 assert_eq!(golden_extent, size, "golden extent drift");
106 let threshold = PixelDiffThreshold::new(DIFF_TOLERANCE);
107 let Ok(report) = PixelDiff::compare(&frame, &golden_rgba, threshold) else {
108 panic!("PixelDiff rejected inputs");
109 };
110 assert!(
111 report.is_clean(),
112 "rectangle render drifted from golden: {} mismatches, worst {:?}, backend {}",
113 report.over_threshold(),
114 report.worst(),
115 frame.backend(),
116 );
117}