forked from 1b-agent/opencadstudio-upstream
fix(otrack): add 250ms dwell timeout before acquiring a tracking point
A tracking point was created as soon as a snap point was highlighted, so moving the mouse across the view spawned accidental tracking points. Acquisition now requires the cursor to rest near the snap point for 250ms (wall-clock) rather than a fixed number of move events. Closes #110 Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
e71ca80ed4
commit
ca8d249173
2 files changed
+33
-13
No files matched your search
+6
-2
@@ -2481,8 +2481,12 @@ impl OpenCADStudio {
|
||||
// distance can place a point along it — issue #69).
|
||||
let otrack_hit = {
|
||||
let snap_world = self.tabs[i].snap_result.map(|s| s.world);
|
||||
self.snapper
|
||||
.update_otrack_dwell(snap_world, view_proj, bounds);
|
||||
self.snapper.update_otrack_dwell(
|
||||
snap_world,
|
||||
view_proj,
|
||||
bounds,
|
||||
Instant::now(),
|
||||
);
|
||||
if self.tabs[i].snap_result.is_none() {
|
||||
let step = if self.polar_mode {
|
||||
Some(self.polar_increment_deg)
|
||||
|
||||
+27
-11
@@ -5,6 +5,7 @@
|
||||
//! Extension, Insertion, Perpendicular, Nearest, ApparentIntersection, Grid, Tangent
|
||||
|
||||
use glam::{Mat4, Vec3};
|
||||
use iced::time::Instant;
|
||||
use iced::{Point, Rectangle};
|
||||
|
||||
use crate::command::TangentObject;
|
||||
@@ -98,8 +99,10 @@ pub struct Snapper {
|
||||
pub tracking_points: Vec<Vec3>,
|
||||
/// Last snap world position (for dwell detection).
|
||||
pub last_snap_world: Option<Vec3>,
|
||||
/// How many consecutive moves the cursor has been near last_snap_world.
|
||||
pub dwell_count: u32,
|
||||
/// When the cursor first rested near `last_snap_world`.
|
||||
pub dwell_since: Option<Instant>,
|
||||
/// Whether the current dwell already acquired/removed a point (fire once).
|
||||
pub dwell_acquired: bool,
|
||||
}
|
||||
|
||||
impl Default for Snapper {
|
||||
@@ -121,7 +124,8 @@ impl Default for Snapper {
|
||||
otrack_enabled: false,
|
||||
tracking_points: Vec::new(),
|
||||
last_snap_world: None,
|
||||
dwell_count: 0,
|
||||
dwell_since: None,
|
||||
dwell_acquired: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,19 +176,25 @@ impl Snapper {
|
||||
snap_world: Option<Vec3>,
|
||||
view_proj: glam::Mat4,
|
||||
bounds: iced::Rectangle,
|
||||
now: Instant,
|
||||
) {
|
||||
if !self.otrack_enabled {
|
||||
self.dwell_count = 0;
|
||||
self.last_snap_world = None;
|
||||
self.dwell_since = None;
|
||||
self.dwell_acquired = false;
|
||||
return;
|
||||
}
|
||||
const DWELL_THRESHOLD: u32 = 4;
|
||||
// The cursor must rest near a snap point for this long before it is
|
||||
// acquired, so that brushing past snap points while moving the mouse
|
||||
// does not create accidental tracking points.
|
||||
const DWELL_MS: u128 = 250;
|
||||
const DWELL_PX: f32 = 8.0;
|
||||
|
||||
match snap_world {
|
||||
None => {
|
||||
self.dwell_count = 0;
|
||||
self.last_snap_world = None;
|
||||
self.dwell_since = None;
|
||||
self.dwell_acquired = false;
|
||||
}
|
||||
Some(p) => {
|
||||
// Convert to screen to measure pixel distance.
|
||||
@@ -198,8 +208,11 @@ impl Snapper {
|
||||
false
|
||||
};
|
||||
if is_same {
|
||||
self.dwell_count += 1;
|
||||
if self.dwell_count == DWELL_THRESHOLD {
|
||||
let elapsed = self
|
||||
.dwell_since
|
||||
.map_or(0, |t| now.duration_since(t).as_millis());
|
||||
if !self.dwell_acquired && elapsed >= DWELL_MS {
|
||||
self.dwell_acquired = true;
|
||||
// Dwelling over an already-acquired point removes it;
|
||||
// otherwise acquire it (max 4 tracked points).
|
||||
let existing = self.tracking_points.iter().position(|t| {
|
||||
@@ -219,8 +232,9 @@ impl Snapper {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.dwell_count = 1;
|
||||
self.last_snap_world = Some(p);
|
||||
self.dwell_since = Some(now);
|
||||
self.dwell_acquired = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,8 +305,9 @@ impl Snapper {
|
||||
/// Clear all acquired tracking points (e.g. when command ends).
|
||||
pub fn clear_tracking(&mut self) {
|
||||
self.tracking_points.clear();
|
||||
self.dwell_count = 0;
|
||||
self.last_snap_world = None;
|
||||
self.dwell_since = None;
|
||||
self.dwell_acquired = false;
|
||||
}
|
||||
|
||||
/// Only runs Tangent snap — used when a command needs object picks via tangent.
|
||||
@@ -317,7 +332,8 @@ impl Snapper {
|
||||
otrack_enabled: false,
|
||||
tracking_points: Vec::new(),
|
||||
last_snap_world: None,
|
||||
dwell_count: 0,
|
||||
dwell_since: None,
|
||||
dwell_acquired: false,
|
||||
};
|
||||
tmp.snap(cursor_world, cursor_screen, wires, view_proj, bounds)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user