fix(grips): compute grip positions in f64 so they sit on the wire

Grip markers drew visibly offset from the entity on large-coordinate
(UTM-scale) drawings: every Grippable impl cast the f64 entity coordinates
to f32 BEFORE the world-offset subtraction, and at 1e7 magnitude an f32
holds ~0.9 drawing units of rounding error. The wires subtract the offset
in f64 first (precision-preserving), so the grips landed up to ~1 unit
away from the geometry they belong to.

GripDef.world is now DVec3: producers fill it straight from the f64
entity data, refresh_selected_grips subtracts the world offset in f64,
and only the small offset-local result is cast to f32 for screen-space
projection (grips_to_screen / find_hit_grip) and drag state.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-06-11 00:04:37 +03:00
commit 25715d1b03
33 changed files with 898 additions and 817 deletions

View file

@ -1,5 +1,4 @@
use acadrust::entities::{RasterImage, Wipeout};
use glam::Vec3;
use crate::command::EntityTransform;
use crate::entities::common::{center_grip, edit_prop as edit, ro_prop as ro, square_grip};
@ -42,10 +41,6 @@ fn image_corners(
]
}
fn to_f32_3(p: [f64; 3]) -> [f32; 3] {
[p[0] as f32, p[1] as f32, p[2] as f32]
}
/// Rectangle border + X diagonals — used as a placeholder for images.
fn image_wire(corners: [[f64; 3]; 4], with_x: bool) -> Vec<[f64; 3]> {
let [p0, p1, p2, p3] = corners;
@ -139,10 +134,10 @@ impl Grippable for RasterImage {
self.size.y,
);
vec![
square_grip(0, Vec3::from(to_f32_3(corners[0]))),
center_grip(1, Vec3::from(to_f32_3(corners[1]))),
center_grip(2, Vec3::from(to_f32_3(corners[2]))),
center_grip(3, Vec3::from(to_f32_3(corners[3]))),
square_grip(0, glam::DVec3::from(corners[0])),
center_grip(1, glam::DVec3::from(corners[1])),
center_grip(2, glam::DVec3::from(corners[2])),
center_grip(3, glam::DVec3::from(corners[3])),
]
}
@ -329,9 +324,9 @@ impl Grippable for Wipeout {
);
if is_polygon {
let ox = self.insertion_point.x as f32;
let oy = self.insertion_point.y as f32;
let oz = self.insertion_point.z as f32;
let ox = self.insertion_point.x;
let oy = self.insertion_point.y;
let oz = self.insertion_point.z;
// Same image-pixel-space → WCS mapping as `to_truck` so grips
// sit exactly on the rendered polygon vertices.
self.clip_boundary_vertices
@ -340,13 +335,13 @@ impl Grippable for Wipeout {
.map(|(i, v)| {
let cx = v.x + self.size.x * 0.5;
let cy = self.size.y * 0.5 - v.y;
let wx = (self.u_vector.x * cx + self.v_vector.x * cy) as f32;
let wy = (self.u_vector.y * cx + self.v_vector.y * cy) as f32;
let wz = (self.u_vector.z * cx + self.v_vector.z * cy) as f32;
let wx = self.u_vector.x * cx + self.v_vector.x * cy;
let wy = self.u_vector.y * cx + self.v_vector.y * cy;
let wz = self.u_vector.z * cx + self.v_vector.z * cy;
if i == 0 {
square_grip(i, Vec3::new(ox + wx, oy + wy, oz + wz))
square_grip(i, glam::DVec3::new(ox + wx, oy + wy, oz + wz))
} else {
center_grip(i, Vec3::new(ox + wx, oy + wy, oz + wz))
center_grip(i, glam::DVec3::new(ox + wx, oy + wy, oz + wz))
}
})
.collect()
@ -359,10 +354,10 @@ impl Grippable for Wipeout {
self.size.y,
);
vec![
square_grip(0, Vec3::from(to_f32_3(corners[0]))),
center_grip(1, Vec3::from(to_f32_3(corners[1]))),
center_grip(2, Vec3::from(to_f32_3(corners[2]))),
center_grip(3, Vec3::from(to_f32_3(corners[3]))),
square_grip(0, glam::DVec3::from(corners[0])),
center_grip(1, glam::DVec3::from(corners[1])),
center_grip(2, glam::DVec3::from(corners[2])),
center_grip(3, glam::DVec3::from(corners[3])),
]
}
}
@ -451,11 +446,7 @@ impl PropertyEditable for Wipeout {
"wo_class_version",
self.class_version.to_string(),
),
ro(
"Clip Mode",
"wo_clip_mode",
format!("{:?}", self.clip_mode),
),
ro("Clip Mode", "wo_clip_mode", format!("{:?}", self.clip_mode)),
ro(
"Definition",
"wo_def_handle",