mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 17:56:50 -06:00
prioritize bounding-box handles over mirror axis
This commit is contained in:
committed by
bflatastic
parent
63cebd392a
commit
411f5f7680
@@ -56,6 +56,11 @@ impl MirrorAxis {
|
||||
if axis.ends.iter().any(|q| q.distance(p) <= cx.tol(9.0)) {
|
||||
return Some((axis, true));
|
||||
}
|
||||
// The center is its own move handle. Keep it ahead of bounding-box handles when the two
|
||||
// happen to overlap; the rest of the axis line yields to those handles below.
|
||||
if axis.center.distance(p) <= cx.tol(7.0) {
|
||||
return Some((axis, false));
|
||||
}
|
||||
let d = axis.ends[1] - axis.ends[0];
|
||||
let length = d.hypot();
|
||||
if !length.is_finite() || length <= 0.0 {
|
||||
@@ -63,7 +68,7 @@ impl MirrorAxis {
|
||||
}
|
||||
let unit = d / length;
|
||||
let along = (p - axis.ends[0]).dot(unit).clamp(0.0, length);
|
||||
((axis.ends[0] + unit * along).distance(p) <= cx.tol(7.0)).then_some((axis, false))
|
||||
((axis.ends[0] + unit * along).distance(p) <= cx.tol(7.0) && !crate::select::bbox_handle_at(cx, p)).then_some((axis, false))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -296,6 +296,12 @@ fn box_hit(cx: &ToolContext, b: &OrientedBox, p: Point) -> Option<BoxHit> {
|
||||
in_rotate_zone(b.rect, lp, tol, cx.tol(18.0)).map(|_| BoxHit::Rotate)
|
||||
}
|
||||
|
||||
/// Whether the current selection's bounding-box handle is under `p`, using the same geometry as
|
||||
/// the Selection tool's press handling.
|
||||
pub(crate) fn bbox_handle_at(cx: &ToolContext, p: Point) -> bool {
|
||||
cx.show_bbox && selection_box(cx).is_some_and(|b| matches!(box_hit(cx, &b, p), Some(BoxHit::Handle(_))))
|
||||
}
|
||||
|
||||
/// How near (screen points) the pointer must come to an object's centre or anchor for Smart Guides
|
||||
/// to label it while hovering.
|
||||
const HOVER_REACH: f64 = 6.0;
|
||||
|
||||
@@ -5041,6 +5041,34 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_bounding_box_handle_scales_a_default_mirror_repeat() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.run("file.new", json!({"width": 400, "height": 300})).unwrap();
|
||||
app.run("shape.rectangle", json!({"x": 100, "y": 100, "width": 100, "height": 100})).unwrap();
|
||||
app.run("object.repeat.mirror", json!({})).unwrap();
|
||||
app.ui.task_bar = false;
|
||||
app.ui.view.rulers = false;
|
||||
let ctx = egui::Context::default();
|
||||
frame(&mut app, &ctx, vec![]);
|
||||
|
||||
let initial = app.selection_bounds().unwrap();
|
||||
let bx = app.selection_box().unwrap();
|
||||
let top = bx.to_doc() * vectorcraft_tools::bbox::Handle::Top.pos(bx.rect);
|
||||
let xf = Xf::new(app.canvas_rect.unwrap(), app.view().unwrap());
|
||||
let from = xf.to_screen(top);
|
||||
let to = from + vec2(0.0, -30.0);
|
||||
let button = |pos, pressed| egui::Event::PointerButton { pos, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() };
|
||||
|
||||
frame(&mut app, &ctx, vec![egui::Event::PointerMoved(from), button(from, true)]);
|
||||
frame(&mut app, &ctx, vec![egui::Event::PointerMoved(to)]);
|
||||
frame(&mut app, &ctx, vec![button(to, false)]);
|
||||
|
||||
let scaled = app.selection_bounds().unwrap();
|
||||
assert!(scaled.height() > initial.height() + 10.0, "Top handle should scale the repeat: {initial:?} -> {scaled:?}");
|
||||
assert!((scaled.width() - initial.width()).abs() < 0.01, "Top handle should not scale width: {initial:?} -> {scaled:?}");
|
||||
}
|
||||
|
||||
/// #1236: Curvature edits points, so existing paths show one circular marker per point and
|
||||
/// no direction handles; switching back to Direct Selection keeps its normal handles.
|
||||
#[test]
|
||||
|
||||
+3
-2
File diff suppressed because one or more lines are too long
+2
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3
-2
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user