feat: Object Snap Tracking (OTRACK / F11)
- snap/mod.rs: Snapper gains otrack_enabled, tracking_points, dwell state;
update_otrack_dwell() acquires a snap point after DWELL_THRESHOLD=4
consecutive moves near the same position; otrack_snap() returns an
H/V alignment snap position; clear_tracking() resets state on command end
- overlay.rs: selection_overlay accepts ost_points + cursor_screen;
draws light-blue dashed H/V lines from acquired points + small cross marker
- statusbar.rs: OTRACK pill (F11) added next to DYN
- view.rs: projects tracking_points to screen, passes to selection_overlay
- update.rs: calls update_otrack_dwell + otrack_snap in ViewportMove;
overrides effective point with OST alignment when no normal snap is active
- ROADMAP.md: Object Snap Tracking marked ✅
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d2a58bdd08
commit
f73d38f0a2
7 changed files with 211 additions and 3 deletions
|
|
@ -234,6 +234,8 @@ pub enum Message {
|
|||
SetPolarAngle(f32),
|
||||
/// Toggle dynamic input overlay (F12).
|
||||
ToggleDynInput,
|
||||
/// Toggle object snap tracking (F11).
|
||||
ToggleOTrack,
|
||||
/// Toggle an individual snap mode (from popup row click).
|
||||
ToggleSnap(crate::snap::SnapType),
|
||||
/// Open / close the OSNAP popup (▾ arrow click).
|
||||
|
|
|
|||
|
|
@ -1010,6 +1010,24 @@ impl H7CAD {
|
|||
} else {
|
||||
self.snapper.snap(cursor_world, p, &all_wires, view_proj, bounds)
|
||||
};
|
||||
|
||||
// Object Snap Tracking: update dwell and override snap if tracking.
|
||||
let otrack_snap_world = {
|
||||
let snap_world = self.tabs[i].snap_result.map(|s| s.world);
|
||||
self.snapper.update_otrack_dwell(snap_world, view_proj, bounds);
|
||||
if self.tabs[i].snap_result.is_none() {
|
||||
self.snapper.otrack_snap(cursor_world, view_proj, bounds)
|
||||
.map(|(w, _)| w)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
if let Some(ow) = otrack_snap_world {
|
||||
// Override the effective point with the OST alignment.
|
||||
// (don't set snap_result so the normal snap marker stays hidden)
|
||||
self.tabs[i].last_cursor_world = ow;
|
||||
}
|
||||
|
||||
let effective = {
|
||||
// snap.world is paper-space for viewport-projected wires; convert
|
||||
// to model-space so previews use consistent coordinates.
|
||||
|
|
@ -1660,6 +1678,13 @@ impl H7CAD {
|
|||
Task::none()
|
||||
}
|
||||
Message::ToggleDynInput => { self.dyn_input ^= true; Task::none() }
|
||||
Message::ToggleOTrack => {
|
||||
self.snapper.otrack_enabled ^= true;
|
||||
if !self.snapper.otrack_enabled {
|
||||
self.snapper.clear_tracking();
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::SetPolarAngle(deg) => {
|
||||
self.polar_increment_deg = deg;
|
||||
self.polar_mode = true;
|
||||
|
|
|
|||
|
|
@ -83,7 +83,23 @@ impl H7CAD {
|
|||
None
|
||||
};
|
||||
|
||||
overlay::selection_overlay(sel, snap_info, grips, grid, ucs_icon)
|
||||
// OST tracking points → screen positions.
|
||||
let ost_points: Vec<overlay::OstTrackPoint> = if self.snapper.otrack_enabled {
|
||||
let vp_mat = tab.scene.camera.borrow().view_proj(vp_bounds);
|
||||
self.snapper.tracking_points.iter().map(|&wp| {
|
||||
let ndc = vp_mat.project_point3(wp);
|
||||
overlay::OstTrackPoint {
|
||||
screen: iced::Point::new(
|
||||
(ndc.x + 1.0) * 0.5 * vp_bounds.width,
|
||||
(1.0 - ndc.y) * 0.5 * vp_bounds.height,
|
||||
),
|
||||
}
|
||||
}).collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
overlay::selection_overlay(sel, snap_info, grips, grid, ucs_icon, ost_points, tab.last_cursor_screen)
|
||||
};
|
||||
|
||||
let nav = container(overlay::nav_toolbar())
|
||||
|
|
@ -202,6 +218,7 @@ impl H7CAD {
|
|||
self.polar_increment_deg,
|
||||
self.show_grid,
|
||||
self.dyn_input,
|
||||
self.snapper.otrack_enabled,
|
||||
tab.scene.layout_names(),
|
||||
tab.scene.current_layout.clone(),
|
||||
self.layout_rename_state.as_ref(),
|
||||
|
|
@ -365,6 +382,9 @@ impl H7CAD {
|
|||
keyboard::Key::Named(keyboard::key::Named::F10) => {
|
||||
Some(Message::TogglePolar)
|
||||
}
|
||||
keyboard::Key::Named(keyboard::key::Named::F11) => {
|
||||
Some(Message::ToggleOTrack)
|
||||
}
|
||||
keyboard::Key::Named(keyboard::key::Named::F12) => {
|
||||
Some(Message::ToggleDynInput)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue