From 94940ad77c8891e54ceb14f6fdb687b4986502b2 Mon Sep 17 00:00:00 2001 From: Karim Jerbi Date: Sat, 17 Oct 2026 09:54:30 +0100 Subject: [PATCH] fix(blocks): show directional Flip grip arrows Use the existing triangular grip marker in Test Block and saved drawing references. Point it across the reflection line, transform it with the INSERT, and reverse the arrow when the native Flip state changes. Validate arrow shape and both directions through the existing mouse-click, undo/redo and reopen regression in both contexts. --- src/app/block_actions.rs | 24 ++++++++++++------------ src/app/block_editor_tests.rs | 22 ++++++++++++++++++++++ src/scene/block_actions.rs | 9 +++++++++ 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/app/block_actions.rs b/src/app/block_actions.rs index 86d0dc2bb..096e71f9e 100644 --- a/src/app/block_actions.rs +++ b/src/app/block_actions.rs @@ -242,12 +242,12 @@ impl OpenCADStudio { id: TEST_GRIP_START + index, world, is_midpoint: false, - shape: if p.flip { - crate::scene::model::object::GripShape::Square - } else { - crate::scene::model::object::GripShape::Triangle - }, - dir: None, + shape: crate::scene::model::object::GripShape::Triangle, + dir: actions::grip_direction(&p, value).and_then(|direction| { + (actions::v(transform.apply(actions::native(direction))) + - actions::v(transform.apply(codec::types::Vector3::ZERO))) + .try_normalize() + }), axis: None, }); } @@ -336,12 +336,12 @@ impl OpenCADStudio { id: TEST_GRIP_START + index, world, is_midpoint: false, - shape: if p.flip { - crate::scene::model::object::GripShape::Square - } else { - crate::scene::model::object::GripShape::Triangle - }, - dir: None, + shape: crate::scene::model::object::GripShape::Triangle, + dir: actions::grip_direction(&p, value).and_then(|direction| { + (actions::v(transform.apply(actions::native(direction))) + - actions::v(transform.apply(codec::types::Vector3::ZERO))) + .try_normalize() + }), axis: None, }); } diff --git a/src/app/block_editor_tests.rs b/src/app/block_editor_tests.rs index b23de5435..75316765c 100644 --- a/src/app/block_editor_tests.rs +++ b/src/app/block_editor_tests.rs @@ -270,12 +270,34 @@ fn block_actions_rotate_flip_mouse_grips_authoring_history_and_reopen() { app.tabs[i].scene.selected.insert(insert); app.refresh_properties(); let depth = app.tabs[i].history.undo_stack.len(); + let flip_grip = app.tabs[i] + .selected_grips + .iter() + .find(|g| { + super::block_actions::is_test_grip(g.id) && g.world.distance(flip.reference) < 1e-5 + }) + .unwrap(); + assert_eq!( + flip_grip.shape, + crate::scene::model::object::GripShape::Triangle + ); + assert!(flip_grip.dir.unwrap().distance(glam::DVec3::Y) < 1e-5); click(&mut app, flip.reference); assert!( app.tabs[i].active_cmd.is_none(), "Flip toggles immediately without point input" ); check(&app, insert, -Vector3::UNIT_Y); + app.tabs[i].scene.selected.insert(insert); + app.refresh_properties(); + let flip_grip = app.tabs[i] + .selected_grips + .iter() + .find(|g| { + super::block_actions::is_test_grip(g.id) && g.world.distance(flip.reference) < 1e-5 + }) + .unwrap(); + assert!(flip_grip.dir.unwrap().distance(-glam::DVec3::Y) < 1e-5); assert_eq!(app.tabs[i].history.undo_stack.len(), depth + 1); app.undo_steps(1); check(&app, insert, Vector3::UNIT_Y); diff --git a/src/scene/block_actions.rs b/src/scene/block_actions.rs index 76160baf1..ca01b0c22 100644 --- a/src/scene/block_actions.rs +++ b/src/scene/block_actions.rs @@ -172,6 +172,15 @@ pub(crate) fn grip_position(p: &Parameter, value: Value) -> DVec3 { } } +/// A Flip arrow points across its reflection line and reverses with its state. +pub(crate) fn grip_direction(p: &Parameter, value: Value) -> Option { + let Value::Flip(flipped) = value else { + return None; + }; + let across = DVec3::Z.cross(p.end - p.base).try_normalize()?; + Some(if flipped { -across } else { across }) +} + pub(crate) fn parameters(doc: &CadDocument, definition: Handle, kind: Kind) -> Vec { let Ok(Some(graph)) = authoring::graph(doc, definition) else { return vec![];