fix(polar): only snap near a polar ray (#70)
POLAR locked the cursor to the nearest angle everywhere, so it could never point between angles. Snap only when the cursor is within the snap aperture (in screen pixels) of a polar ray; otherwise leave it free, so POLAR behaves as if off when pointing away from every angle. The polar guide line now shows only while actually snapped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
f2fd36b5fa
commit
3c97111d8f
2 changed files with 61 additions and 5 deletions
|
|
@ -150,6 +150,34 @@ pub(super) fn polar_constrain(pt: glam::Vec3, base: glam::Vec3, step_deg: f32) -
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Polar constraint that only engages when the cursor is within `tol_px`
|
||||||
|
/// screen pixels of the nearest polar ray; otherwise the cursor is left free
|
||||||
|
/// so POLAR behaves as if off when pointing away from every angle (issue #70).
|
||||||
|
pub(super) fn polar_constrain_near(
|
||||||
|
pt: glam::Vec3,
|
||||||
|
base: glam::Vec3,
|
||||||
|
step_deg: f32,
|
||||||
|
view_proj: glam::Mat4,
|
||||||
|
bounds: iced::Rectangle,
|
||||||
|
tol_px: f32,
|
||||||
|
) -> glam::Vec3 {
|
||||||
|
let snapped = polar_constrain(pt, base, step_deg);
|
||||||
|
let to_screen = |w: glam::Vec3| {
|
||||||
|
let ndc = view_proj.project_point3(w);
|
||||||
|
(
|
||||||
|
(ndc.x + 1.0) * 0.5 * bounds.width,
|
||||||
|
(1.0 - ndc.y) * 0.5 * bounds.height,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let (cx, cy) = to_screen(pt);
|
||||||
|
let (sx, sy) = to_screen(snapped);
|
||||||
|
if ((cx - sx).powi(2) + (cy - sy).powi(2)).sqrt() <= tol_px {
|
||||||
|
snapped
|
||||||
|
} else {
|
||||||
|
pt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── Clipboard / selection helpers ──────────────────────────────────────────
|
// ── Clipboard / selection helpers ──────────────────────────────────────────
|
||||||
|
|
||||||
/// Compute the centroid of a set of wire models (average of all points).
|
/// Compute the centroid of a set of wire models (average of all points).
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use super::helpers::{
|
use super::helpers::{
|
||||||
ortho_constrain, parse_coord, polar_constrain, ucs_rotate_vec, ucs_to_wcs, ucs_z_axis,
|
ortho_constrain, parse_coord, polar_constrain_near, ucs_rotate_vec, ucs_to_wcs, ucs_z_axis,
|
||||||
CoordKind,
|
CoordKind,
|
||||||
};
|
};
|
||||||
use super::{Message, OpenCADStudio, POLY_START_DELAY_MS};
|
use super::{Message, OpenCADStudio, POLY_START_DELAY_MS};
|
||||||
|
|
@ -2253,7 +2253,14 @@ impl OpenCADStudio {
|
||||||
if self.ortho_mode {
|
if self.ortho_mode {
|
||||||
snapped = ortho_constrain(snapped, base);
|
snapped = ortho_constrain(snapped, base);
|
||||||
} else if self.polar_mode {
|
} else if self.polar_mode {
|
||||||
snapped = polar_constrain(snapped, base, self.polar_increment_deg);
|
snapped = polar_constrain_near(
|
||||||
|
snapped,
|
||||||
|
base,
|
||||||
|
self.polar_increment_deg,
|
||||||
|
vp_mat,
|
||||||
|
bounds,
|
||||||
|
self.snapper.snap_radius_px,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2440,7 +2447,14 @@ impl OpenCADStudio {
|
||||||
if self.ortho_mode {
|
if self.ortho_mode {
|
||||||
pt = ortho_constrain(pt, base);
|
pt = ortho_constrain(pt, base);
|
||||||
} else if self.polar_mode {
|
} else if self.polar_mode {
|
||||||
pt = polar_constrain(pt, base, self.polar_increment_deg);
|
pt = polar_constrain_near(
|
||||||
|
pt,
|
||||||
|
base,
|
||||||
|
self.polar_increment_deg,
|
||||||
|
view_proj,
|
||||||
|
bounds,
|
||||||
|
self.snapper.snap_radius_px,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pt
|
pt
|
||||||
|
|
@ -2484,7 +2498,14 @@ impl OpenCADStudio {
|
||||||
if let Some(base) = self.last_point {
|
if let Some(base) = self.last_point {
|
||||||
let dx = effective.x - base.x;
|
let dx = effective.x - base.x;
|
||||||
let dy = effective.y - base.y;
|
let dy = effective.y - base.y;
|
||||||
if (dx * dx + dy * dy).sqrt() > 1e-4 {
|
// Only show the guide while POLAR is actually engaged
|
||||||
|
// — i.e. the point is snapped onto a polar ray, not
|
||||||
|
// floating free near no angle (issue #70).
|
||||||
|
let step = self.polar_increment_deg.to_radians();
|
||||||
|
let angle = dy.atan2(dx);
|
||||||
|
let snapped = step > 1e-6
|
||||||
|
&& ((angle / step).round() * step - angle).abs() < 1e-3;
|
||||||
|
if snapped && (dx * dx + dy * dy).sqrt() > 1e-4 {
|
||||||
let far = 1e5_f32;
|
let far = 1e5_f32;
|
||||||
let dir = glam::Vec3::new(dx, dy, 0.0).normalize();
|
let dir = glam::Vec3::new(dx, dy, 0.0).normalize();
|
||||||
let far_pos = base + dir * far;
|
let far_pos = base + dir * far;
|
||||||
|
|
@ -2911,7 +2932,14 @@ impl OpenCADStudio {
|
||||||
if self.ortho_mode {
|
if self.ortho_mode {
|
||||||
pt = ortho_constrain(pt, base);
|
pt = ortho_constrain(pt, base);
|
||||||
} else if self.polar_mode {
|
} else if self.polar_mode {
|
||||||
pt = polar_constrain(pt, base, self.polar_increment_deg);
|
pt = polar_constrain_near(
|
||||||
|
pt,
|
||||||
|
base,
|
||||||
|
self.polar_increment_deg,
|
||||||
|
vp_mat,
|
||||||
|
bounds,
|
||||||
|
self.snapper.snap_radius_px,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pt
|
pt
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue