feat(snap,pick): eye-relative CPU projection — drop full view_proj at UTM

Route the remaining absolute-coordinate CPU projection through the
relative-to-eye path so it no longer multiplies by a view_proj matrix
carrying a ~1e7 translation (which cancels catastrophically in f32 after
world_offset removal):
- camera pick_on_plane / pick_on_target_plane delegate to
  unproject_on_plane_f64 (rotation-only inverse + f64 eye)
- snapper (world_to_screen), hit_test (click_hit / mesh / hatch), the
  scene mesh/solid hit methods, grid + UCS axes, and polar_constrain_near
  all take view_rot + eye and project relative-to-eye
- compute_grid_step measures the per-unit scale via the rotation-only
  matrix (no eye term needed)

view_proj remains only for conservative CPU frustum-cull / scissor and the
screen-space viewcube / UCS-icon directions. NOTE: SnapResult.world and
the command-point chain are still Vec3 (f32) — a ~0.5 m quantization at
extreme UTM zoom remains; the command layer goes f64 next.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 4.7 committed 2026-06-23 23:33:21 +03:00
1 parent 52882749eb
commit 96aaf2a293
7 files changed
+187 -177

No files matched your search

+5 -2
View File
@@ -69,10 +69,13 @@ pub struct GridParams {
/// Returns the smallest power-of-5 multiple of 1.0 that places grid lines at
/// least `MIN_GRID_PX` pixels apart. This matches exactly what `draw_grid`
/// renders, so callers can sync snap spacing to the visible grid.
pub fn compute_grid_step(vp: Mat4, bounds: iced::Rectangle) -> f32 {
pub fn compute_grid_step(view_rot: Mat4, bounds: iced::Rectangle) -> f32 {
use glam::Vec3;
// Only the per-unit screen scale is needed; project small eye-relative
// offsets (0 / X / Y) through the rotation-only matrix so this stays correct
// and precise at any absolute coordinate (no eye term required).
let w2s = |world: Vec3| {
let ndc = vp.project_point3(world);
let ndc = view_rot.project_point3(world);
glam::Vec2::new(
(ndc.x + 1.0) * 0.5 * bounds.width,
(1.0 - ndc.y) * 0.5 * bounds.height,