Another project
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 5.9 kB View raw
1use core::time::Duration; 2 3use bone_kernel::{ 4 BrepSolid, Curve2Kind, ExtrudeDirection, ExtrudeEndCondition, ExtrudeFeature, ExtrudeProfile, 5 ExtrudeSense, Line2, MergeResult, ProfileEdge, ProfileLoop, evaluate_extrude, 6}; 7use bone_render::{ 8 CameraTween, EdgeScene, OffscreenContext, SnapshotFrame, SolidRenderer, SolidScene, Style, 9 frame_standard_view, 10}; 11use bone_types::{ 12 Aabb3, AngleTolerance, Camera3, ChordHeightTolerance, CubicEasing, DisplayMode, FeatureId, 13 Length, Point2, Point3, PositiveLength, SketchEntityId, SketchId, StandardView, Tolerance, 14 UnitVec3, millimeter, 15}; 16use slotmap::{Key, SlotMap}; 17 18mod common; 19 20use common::{check_golden, extent_square as extent, make_context}; 21 22const TOLERANCE: Tolerance = Tolerance::new(1.0e-9); 23const CHORD_MM: f64 = 0.05; 24const ANGLE_RAD: f64 = 0.2; 25const UPDATE_ENV: &str = "BONE_UPDATE_STANDARD_VIEW_GOLDENS"; 26 27fn slab() -> BrepSolid { 28 let mut entities: SlotMap<SketchEntityId, ()> = SlotMap::with_key(); 29 let mut features: SlotMap<FeatureId, ()> = SlotMap::with_key(); 30 let Ok(plane) = bone_types::Plane3::new( 31 Point3::origin(), 32 UnitVec3::x_axis(), 33 UnitVec3::y_axis(), 34 TOLERANCE, 35 ) else { 36 panic!("x and y axes are orthonormal"); 37 }; 38 let corners = [ 39 Point2::from_mm(0.0, 0.0), 40 Point2::from_mm(2.0, 0.0), 41 Point2::from_mm(2.0, 1.0), 42 Point2::from_mm(0.0, 1.0), 43 ]; 44 let edges = (0..4) 45 .map(|index| { 46 let start = corners[index]; 47 let end = corners[(index + 1) % 4]; 48 let Ok(segment) = Line2::new(start, end, TOLERANCE) else { 49 panic!("rectangle endpoints are distinct"); 50 }; 51 ProfileEdge::new( 52 Curve2Kind::Line(segment), 53 entities.insert(()), 54 entities.insert(()), 55 ) 56 }) 57 .collect(); 58 let profile = ExtrudeProfile::new(plane, vec![ProfileLoop::Open(edges)]); 59 let Ok(depth) = PositiveLength::new(Length::new::<millimeter>(3.0)) else { 60 panic!("3 mm is a positive length"); 61 }; 62 let feature = ExtrudeFeature { 63 sketch: SketchId::null(), 64 direction: ExtrudeDirection::Normal { 65 sense: ExtrudeSense::Forward, 66 }, 67 end_condition: ExtrudeEndCondition::Blind { depth }, 68 draft: None, 69 thin_wall: None, 70 merge_result: MergeResult::Merge, 71 }; 72 let Ok(solid) = evaluate_extrude(features.insert(()), &profile, &feature) else { 73 panic!("the rectangle extrudes into a 2x1x3 slab"); 74 }; 75 solid 76} 77 78fn aabb_of(solid: &BrepSolid) -> Aabb3 { 79 let Some(aabb) = solid.bounding_box() else { 80 panic!("the slab has a bounding box"); 81 }; 82 aabb 83} 84 85fn scenes(solid: &BrepSolid) -> (SolidScene, EdgeScene) { 86 let Ok(mesh) = solid.tessellate( 87 ChordHeightTolerance::from_mm(CHORD_MM), 88 AngleTolerance::from_radians(ANGLE_RAD), 89 ) else { 90 panic!("the solid tessellates"); 91 }; 92 let Ok(faces) = SolidScene::from_mesh(&mesh) else { 93 panic!("the mesh packs face pick ids"); 94 }; 95 let Ok(edges) = EdgeScene::from_solid(solid, &mesh, ChordHeightTolerance::from_mm(CHORD_MM)) 96 else { 97 panic!("the solid packs edge pick ids"); 98 }; 99 (faces, edges) 100} 101 102fn render( 103 ctx: &OffscreenContext, 104 faces: &SolidScene, 105 edges: &EdgeScene, 106 camera: Camera3, 107) -> SnapshotFrame { 108 let mut renderer = SolidRenderer::new(ctx.gpu(), ctx.color_format()); 109 let Ok(frame) = renderer.render_display( 110 ctx, 111 faces, 112 edges, 113 camera, 114 &Style::default(), 115 DisplayMode::ShadedWithEdges, 116 ) else { 117 panic!("SolidRenderer::render_display failed"); 118 }; 119 frame 120} 121 122fn view(solid: &BrepSolid, which: StandardView, size: bone_render::ViewportExtent) -> Camera3 { 123 let Ok(camera) = frame_standard_view(aabb_of(solid), size, which, None) else { 124 panic!("a fixed standard view frames the slab"); 125 }; 126 camera 127} 128 129#[test] 130fn front_view_matches_golden() { 131 let size = extent(256); 132 let ctx = make_context(size); 133 let solid = slab(); 134 let (faces, edges) = scenes(&solid); 135 let frame = render( 136 &ctx, 137 &faces, 138 &edges, 139 view(&solid, StandardView::Front, size), 140 ); 141 check_golden( 142 &frame, 143 "tests/goldens/standard_view_front_256.png", 144 UPDATE_ENV, 145 ); 146} 147 148#[test] 149fn top_view_matches_golden() { 150 let size = extent(256); 151 let ctx = make_context(size); 152 let solid = slab(); 153 let (faces, edges) = scenes(&solid); 154 let frame = render(&ctx, &faces, &edges, view(&solid, StandardView::Top, size)); 155 check_golden( 156 &frame, 157 "tests/goldens/standard_view_top_256.png", 158 UPDATE_ENV, 159 ); 160} 161 162#[test] 163fn right_view_matches_golden() { 164 let size = extent(256); 165 let ctx = make_context(size); 166 let solid = slab(); 167 let (faces, edges) = scenes(&solid); 168 let frame = render( 169 &ctx, 170 &faces, 171 &edges, 172 view(&solid, StandardView::Right, size), 173 ); 174 check_golden( 175 &frame, 176 "tests/goldens/standard_view_right_256.png", 177 UPDATE_ENV, 178 ); 179} 180 181#[test] 182fn front_to_top_tween_midpoint_matches_golden() { 183 let size = extent(256); 184 let ctx = make_context(size); 185 let solid = slab(); 186 let (faces, edges) = scenes(&solid); 187 let tween = CameraTween::eased( 188 view(&solid, StandardView::Front, size), 189 view(&solid, StandardView::Top, size), 190 Duration::from_millis(180), 191 CubicEasing::STANDARD, 192 ); 193 let Ok(mid) = tween.sample(Duration::from_millis(90)) else { 194 panic!("the tween samples a valid midpoint camera"); 195 }; 196 let frame = render(&ctx, &faces, &edges, mid); 197 check_golden( 198 &frame, 199 "tests/goldens/view_tween_front_to_top_mid_256.png", 200 UPDATE_ENV, 201 ); 202}