Snap 3D bridge lines to points on other sketch planes (#229)

* Snap 3D bridge drawing to points on visible sketch planes

* Keep cross-sketch point picks out of sketch tools

`pick()` returned every visible sketch's points while 3D drawing was on,
so `sketch_point_at` and the sketch tools could take another sketch's
point as an active-sketch local point plus id (flattening it or making a
bad coincident reference). `pick()` is local again; the bridge snap uses
`pick_3d` explicitly and hover mirrors it for feedback. `sketch_point_at`
also ignores foreign points defensively.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(sketch3d): check local point scope with a sketch tool active

Main's Project preselection fix now offers other sketches' points to idle
selection, so assert the local scope where it still applies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style: rustfmt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-10-12 11:25:39 -04:00
1 parent cb8aa38cc9
commit e6e0a7ebb5
5 files changed
+163 -6

No files matched your search

+20 -3
View File
@@ -682,6 +682,7 @@ fn pick_cached(app: &mut SolveApp, proj: &Proj, pos: Pos2) -> Vec<Hit> {
app.session.revision.hash(&mut h);
(std::sync::Arc::as_ptr(&app.session.world_state()) as usize).hash(&mut h);
app.session.active_sketch.hash(&mut h);
crate::sketch3d::drawing(app).hash(&mut h);
app.dialog.as_ref().and_then(|d| d.active_input()).map(|i| i.accept).hash(&mut h);
app.session.visibility.hidden_bodies.hash(&mut h);
app.session.visibility.hidden_occurrences.hash(&mut h);
@@ -694,21 +695,37 @@ fn pick_cached(app: &mut SolveApp, proj: &Proj, pos: Pos2) -> Vec<Hit> {
{
return hits.clone();
}
let hits = pick(app, proj, pos);
// Hover feedback mirrors what a 3D bridge click will snap to; clicks still go through `pick`.
let hits = pick_with_sketch_points(app, proj, pos, crate::sketch3d::drawing(app));
app.viewport.last_pick = Some((key, hits.clone()));
hits
}
/// What is under `pos`. Sketch points come from the active sketch only (from every shown sketch
/// while a dialog input takes points, while idle, or while projecting), so sketch tools never get
/// another sketch's local point and id. The 3D bridge snap asks for cross-sketch points explicitly with [`pick_3d`].
pub fn pick(app: &SolveApp, proj: &Proj, pos: Pos2) -> Vec<Hit> {
pick_with_sketch_points(app, proj, pos, false)
}
/// Like [`pick`], but also returns points of every visible sketch: only for the 3D drawing snap,
/// which converts them to world space through their own sketch's plane.
pub fn pick_3d(app: &SolveApp, proj: &Proj, pos: Pos2) -> Vec<Hit> {
pick_with_sketch_points(app, proj, pos, true)
}
fn pick_with_sketch_points(app: &SolveApp, proj: &Proj, pos: Pos2, other_sketch_points: bool) -> Vec<Hit> {
let s = &app.session;
let st = s.world_state();
let bounds = body_bounds(app, &st);
let bound = |name: &str| bounds.iter().find(|(n, _)| n == name).map(|(_, b)| *b);
let mut hits = Vec::new();
let (o, d) = proj.ray(pos);
// Sketch points of the active sketch (of every shown sketch while a command takes points).
// Sketch points of the active sketch (of every shown sketch while a command takes points,
// while idle or projecting, and while 3D drawing snaps to points on other visible sketches).
let wants_points = app.dialog.as_ref().and_then(|d| d.active_input()).is_some_and(|i| i.accept & crate::selection::POINTS != 0);
let external_points = wants_points || (app.dialog.is_none() && app.tool.as_ref().is_none_or(|tool| tool.cmd == "sketch.project"));
let external_points =
wants_points || other_sketch_points || (app.dialog.is_none() && app.tool.as_ref().is_none_or(|tool| tool.cmd == "sketch.project"));
let point_sketches: Vec<u64> = if external_points { visible_sketches(app) } else { s.active_sketch.into_iter().collect() };
let mut best: Option<(f32, Hit)> = None;
for ss in point_sketches.iter().filter_map(|sid| st.sketch(*sid)) {