From a83b327d13660bc0af0c8df8cb86a6558b982a6a Mon Sep 17 00:00:00 2001 From: Nikolajus Krauklis Date: Sat, 14 Nov 2026 12:00:52 +0200 Subject: [PATCH] Issue #56 switching how guides are created for tape measure tool --- .../src/construction_guide_tests.rs | 12 +++++++----- crates/benchup-app/src/measurement_tools.rs | 19 ++++++++++++++++--- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/crates/benchup-app/src/construction_guide_tests.rs b/crates/benchup-app/src/construction_guide_tests.rs index 200e9c3..f973a9e 100644 --- a/crates/benchup-app/src/construction_guide_tests.rs +++ b/crates/benchup-app/src/construction_guide_tests.rs @@ -140,7 +140,7 @@ fn selected_guides_render_a_distinct_highlight() { } #[test] -fn tape_guide_follows_the_tape_from_an_edge_and_supports_exact_entry_and_undo() { +fn tape_guide_from_an_edge_is_parallel_to_it_and_supports_exact_entry_and_undo() { let mut h = Harness::new(); h.app .doc @@ -157,14 +157,16 @@ fn tape_guide_follows_the_tape_from_an_edge_and_supports_exact_entry_and_undo() ); let tape_direction = h.app.snap.unwrap().point.normalize(); assert!(tape_direction.x > 0.1 && tape_direction.y < -0.1); + // Dragging away from a horizontal edge offsets a horizontal guide. let preview = h.app.measurement_guide().unwrap(); - assert!(preview.direction.unwrap().distance(tape_direction) < 1e-6); + assert!(preview.direction.unwrap().dot(DVec3::X).abs() > 1. - 1e-6); h.app.length_input = "250mm".into(); h.key(Key::Enter); let guide = h.app.doc.model.guides.values().next().unwrap(); - assert!(guide.direction.unwrap().distance(tape_direction) < 1e-6); - assert!(guide.origin.distance(tape_direction * 0.25) < 1e-6); - assert!(guide.origin.cross(guide.direction.unwrap()).length() < 1e-6); + assert!(guide.direction.unwrap().dot(DVec3::X).abs() > 1. - 1e-6); + // Exact entry is the perpendicular offset from the edge. + assert!((guide.origin.y + 0.25).abs() < 1e-6); + assert!(guide.origin.z.abs() < 1e-6); assert_eq!(h.app.doc.model.edges, geometry.edges); assert_eq!(h.app.doc.model.vertices, geometry.vertices); assert!(h.app.doc.model.faces.is_empty()); diff --git a/crates/benchup-app/src/measurement_tools.rs b/crates/benchup-app/src/measurement_tools.rs index 29777bc..a6a0f21 100644 --- a/crates/benchup-app/src/measurement_tools.rs +++ b/crates/benchup-app/src/measurement_tools.rs @@ -17,10 +17,15 @@ pub(super) struct Measurement { impl BenchUpApp { /// Use the same infinite line for the live preview and the saved guide. pub(super) fn measurement_guide(&self) -> Option { - self.measurement.as_ref()?; + let reference = self.measurement.as_ref()?.reference_direction; let points = self.measurement_points(); let (origin, direction) = match (self.tool, points.as_slice()) { - (Tool::TapeMeasure, [start, end]) => (*end, (*end - *start).try_normalize()?), + // Like SketchUp, a tape started on an edge offsets a guide parallel + // to that edge; from a point it extends along the tape. + (Tool::TapeMeasure, [start, end]) => ( + *end, + reference.or_else(|| (*end - *start).try_normalize())?, + ), (Tool::Protractor, [center, _, end]) => (*center, (*end - *center).try_normalize()?), _ => return None, }; @@ -130,7 +135,15 @@ impl BenchUpApp { .copied() .or(self.snap.map(|s| s.point)) .ok_or(tr!("measurement_tools.move_the_pointer_to_choose_a_direction_first"))?; - let direction = (endpoint - origin) + let mut tape = endpoint - origin; + // An edge-parallel guide is placed at the typed perpendicular offset. + if let Some(reference) = m.reference_direction { + let offset = tape - reference * tape.dot(reference); + if offset.length() > 1e-7 { + tape = offset; + } + } + let direction = tape .try_normalize() .ok_or(tr!("measurement_tools.move_the_pointer_to_choose_a_direction_first"))?; Ok(origin + direction * length)