Fix perpendicular and OTRACK snap interactions
This commit is contained in:
parent
a8c9e3dd0c
commit
080a82306e
2 changed files with 50 additions and 31 deletions
|
|
@ -1107,10 +1107,8 @@ impl OpenCADStudio {
|
||||||
// Grip edits use the same tracking-point acquisition and OTRACK
|
// Grip edits use the same tracking-point acquisition and OTRACK
|
||||||
// resolution as normal interactive point commands.
|
// resolution as normal interactive point commands.
|
||||||
let otrack_hit = {
|
let otrack_hit = {
|
||||||
let snap_world = snap_hit.map(|s| s.world);
|
|
||||||
|
|
||||||
self.snapper.update_otrack_dwell(
|
self.snapper.update_otrack_dwell(
|
||||||
snap_world,
|
snap_hit,
|
||||||
&snap_candidates,
|
&snap_candidates,
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
|
|
@ -1595,9 +1593,8 @@ impl OpenCADStudio {
|
||||||
// to a tracking ray (and store the alignment so a typed
|
// to a tracking ray (and store the alignment so a typed
|
||||||
// distance can place a point along it — issue #69).
|
// distance can place a point along it — issue #69).
|
||||||
let otrack_hit = {
|
let otrack_hit = {
|
||||||
let snap_world = self.tabs[i].snap_result.map(|s| s.world);
|
|
||||||
self.snapper.update_otrack_dwell(
|
self.snapper.update_otrack_dwell(
|
||||||
snap_world,
|
self.tabs[i].snap_result,
|
||||||
&snap_candidates,
|
&snap_candidates,
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
|
|
@ -1614,7 +1611,11 @@ impl OpenCADStudio {
|
||||||
bounds,
|
bounds,
|
||||||
Instant::now(),
|
Instant::now(),
|
||||||
);
|
);
|
||||||
if self.tabs[i].snap_result.is_none() {
|
if self.tabs[i].snap_result.is_none()
|
||||||
|
|| self.tabs[i]
|
||||||
|
.snap_result
|
||||||
|
.is_some_and(|s| s.snap_type == crate::snap::SnapType::Extension)
|
||||||
|
{
|
||||||
// A window corner is a free point, so neither the
|
// A window corner is a free point, so neither the
|
||||||
// Polar increment nor the Ortho lock may pull the
|
// Polar increment nor the Ortho lock may pull the
|
||||||
// OTRACK alignment onto an axis (#291).
|
// OTRACK alignment onto an axis (#291).
|
||||||
|
|
@ -2992,7 +2993,10 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
// OTRACK alignment wins over ortho/polar; otherwise apply
|
// OTRACK alignment wins over ortho/polar; otherwise apply
|
||||||
// ortho/polar relative to the last point.
|
// ortho/polar relative to the last point.
|
||||||
let otrack = if !world_pt_locked && snap_hit.is_none() {
|
let otrack = if !world_pt_locked
|
||||||
|
&& (snap_hit.is_none()
|
||||||
|
|| snap_hit.is_some_and(|s| s.snap_type == crate::snap::SnapType::Extension))
|
||||||
|
{
|
||||||
let step = if self.polar_mode && !is_window_corner {
|
let step = if self.polar_mode && !is_window_corner {
|
||||||
Some(self.polar_increment_deg)
|
Some(self.polar_increment_deg)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
63
src/snap.rs
63
src/snap.rs
|
|
@ -285,7 +285,7 @@ impl Snapper {
|
||||||
/// `snap_world` is the current snap result world point (if any).
|
/// `snap_world` is the current snap result world point (if any).
|
||||||
pub fn update_otrack_dwell<W: WireSource + ?Sized>(
|
pub fn update_otrack_dwell<W: WireSource + ?Sized>(
|
||||||
&mut self,
|
&mut self,
|
||||||
snap_world: Option<DVec3>,
|
snap: Option<SnapResult>,
|
||||||
wires: &W,
|
wires: &W,
|
||||||
view_rot: glam::Mat4,
|
view_rot: glam::Mat4,
|
||||||
eye: glam::DVec3,
|
eye: glam::DVec3,
|
||||||
|
|
@ -302,6 +302,24 @@ impl Snapper {
|
||||||
self.dwell_acquired = false;
|
self.dwell_acquired = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Only stable geometric snap points may be acquired automatically for
|
||||||
|
// OTRACK. Continuous/contextual snaps such as Nearest, Perpendicular,
|
||||||
|
// Tangent and Extension can move along geometry and would otherwise
|
||||||
|
// create arbitrary tracking points while the cursor is hovering. (#716)
|
||||||
|
let snap_world = snap.and_then(|hit| {
|
||||||
|
matches!(
|
||||||
|
hit.snap_type,
|
||||||
|
SnapType::Endpoint
|
||||||
|
| SnapType::Midpoint
|
||||||
|
| SnapType::Center
|
||||||
|
| SnapType::Node
|
||||||
|
| SnapType::Quadrant
|
||||||
|
| SnapType::Intersection
|
||||||
|
| SnapType::Insertion
|
||||||
|
| SnapType::ApparentIntersection
|
||||||
|
)
|
||||||
|
.then_some(hit.world)
|
||||||
|
});
|
||||||
// With OTRACK off, acquisition is Extension-driven, and Extension tracks
|
// With OTRACK off, acquisition is Extension-driven, and Extension tracks
|
||||||
// a line only from a real segment endpoint — so acquire endpoints only.
|
// a line only from a real segment endpoint — so acquire endpoints only.
|
||||||
// This stops a paused cursor on an extension foot (or a midpoint/centre)
|
// This stops a paused cursor on an extension foot (or a midpoint/centre)
|
||||||
|
|
@ -1186,33 +1204,30 @@ impl Snapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Perpendicular — foot of perpendicular from the drawing base ──
|
// ── Perpendicular — foot of perpendicular from the drawing base ──
|
||||||
// Drop the foot from the point the command is drawing *from* (so the
|
// A perpendicular snap requires a real drawing base. Without one
|
||||||
// new segment is truly perpendicular to the target). Only when there
|
// (for example while picking the first point of LINE) there is no
|
||||||
// is no base point — e.g. picking the very first point — does it fall
|
// meaningful perpendicular direction, so do not offer a candidate.
|
||||||
// back to the cursor (a plain nearest-on-line). The candidate is gated
|
// Falling back to the cursor here would make Perpendicular behave
|
||||||
// on its screen distance to the cursor like every other snap, so it
|
// like Nearest. (#716)
|
||||||
// offers when the cursor is near the perpendicular foot. (#118)
|
|
||||||
if self.is_on(SnapType::Perpendicular) {
|
if self.is_on(SnapType::Perpendicular) {
|
||||||
let q = self
|
if let Some(q) = self.from_point.map(|v| v.as_dvec3()) {
|
||||||
.from_point
|
if let Some(segments) = &local_segments {
|
||||||
.map(|v| v.as_dvec3())
|
for seg in segments {
|
||||||
.unwrap_or(cursor_world);
|
if let Some(foot) = perp_foot(q, seg.a, seg.b) {
|
||||||
if let Some(segments) = &local_segments {
|
|
||||||
for seg in segments {
|
|
||||||
if let Some(foot) = perp_foot(q, seg.a, seg.b) {
|
|
||||||
try_pt(foot, SnapType::Perpendicular);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for wire in wires.iter() {
|
|
||||||
if !wire_in_range(wire) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for i in 0..wire.points.len().saturating_sub(1) {
|
|
||||||
if let Some(foot) = perp_foot(q, wp_f64(wire, i), wp_f64(wire, i + 1)) {
|
|
||||||
try_pt(foot, SnapType::Perpendicular);
|
try_pt(foot, SnapType::Perpendicular);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
for wire in wires.iter() {
|
||||||
|
if !wire_in_range(wire) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for i in 0..wire.points.len().saturating_sub(1) {
|
||||||
|
if let Some(foot) = perp_foot(q, wp_f64(wire, i), wp_f64(wire, i + 1)) {
|
||||||
|
try_pt(foot, SnapType::Perpendicular);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue