forked from 1b-agent/opencadstudio-upstream
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:
1 parent
52882749eb
commit
96aaf2a293
7 files changed
+187
-177
No files matched your search
@@ -225,65 +225,17 @@ impl Camera {
|
||||
plane_normal: Vec3,
|
||||
plane_point: Vec3,
|
||||
) -> Vec3 {
|
||||
let ndc_x = (screen.x / bounds.width) * 2.0 - 1.0;
|
||||
let ndc_y = 1.0 - (screen.y / bounds.height) * 2.0;
|
||||
let inv = self.view_proj(bounds).inverse();
|
||||
|
||||
let (ray_origin, ray_dir) = match self.projection {
|
||||
Projection::Perspective => {
|
||||
let near_pt = inv.project_point3(Vec3::new(ndc_x, ndc_y, 0.0));
|
||||
let far_pt = inv.project_point3(Vec3::new(ndc_x, ndc_y, 1.0));
|
||||
let dir = (far_pt - near_pt).normalize();
|
||||
(near_pt, dir)
|
||||
}
|
||||
Projection::Orthographic => {
|
||||
let origin = inv.project_point3(Vec3::new(ndc_x, ndc_y, 0.0));
|
||||
let forward = (self.target.as_vec3() - self.eye()).normalize();
|
||||
(origin, forward)
|
||||
}
|
||||
};
|
||||
|
||||
let denom = ray_dir.dot(plane_normal);
|
||||
if denom.abs() < 1e-6 {
|
||||
return plane_point;
|
||||
}
|
||||
let t = (plane_point - ray_origin).dot(plane_normal) / denom;
|
||||
if t < 0.0 {
|
||||
return plane_point;
|
||||
}
|
||||
ray_origin + ray_dir * t
|
||||
// Delegate to the eye-relative f64 unproject so the cursor stays
|
||||
// precise at UTM-scale coordinates (the old full view_proj.inverse()
|
||||
// cancelled catastrophically in f32).
|
||||
self.unproject_on_plane_f64(screen, bounds, plane_normal, plane_point.as_dvec3())
|
||||
.as_vec3()
|
||||
}
|
||||
|
||||
pub fn pick_on_target_plane(&self, screen: Point, bounds: Rectangle) -> Vec3 {
|
||||
let ndc_x = (screen.x / bounds.width) * 2.0 - 1.0;
|
||||
let ndc_y = 1.0 - (screen.y / bounds.height) * 2.0;
|
||||
let inv = self.view_proj(bounds).inverse();
|
||||
|
||||
match self.projection {
|
||||
Projection::Perspective => {
|
||||
let target = self.target.as_vec3();
|
||||
let near_pt = inv.project_point3(Vec3::new(ndc_x, ndc_y, 0.0));
|
||||
let far_pt = inv.project_point3(Vec3::new(ndc_x, ndc_y, 1.0));
|
||||
let dir = (far_pt - near_pt).normalize();
|
||||
let forward = (target - self.eye()).normalize();
|
||||
let denom = dir.dot(forward);
|
||||
if denom.abs() < 1e-6 {
|
||||
return target;
|
||||
}
|
||||
let t = (target - near_pt).dot(forward) / denom;
|
||||
if t < 0.0 {
|
||||
return target;
|
||||
}
|
||||
near_pt + dir * t
|
||||
}
|
||||
Projection::Orthographic => {
|
||||
let target = self.target.as_vec3();
|
||||
let ray_origin = inv.project_point3(Vec3::new(ndc_x, ndc_y, 0.0));
|
||||
let forward = (target - self.eye()).normalize();
|
||||
let t = (target - ray_origin).dot(forward) / forward.dot(forward);
|
||||
ray_origin + forward * t
|
||||
}
|
||||
}
|
||||
let forward = (self.target.as_vec3() - self.eye()).normalize_or(Vec3::NEG_Z);
|
||||
self.unproject_on_plane_f64(screen, bounds, forward, self.target)
|
||||
.as_vec3()
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user