wip(f64): convert command layer to DVec3 (in progress)

Migrate the command/snap/grip/cursor chain from f32 Vec3 to f64 DVec3 so
picked points, snaps and grip drags stay precise at UTM-scale coordinates
(f32 quantizes to ~0.5 m there).

Done so far:
- command/mod.rs: EntityTransform, CmdResult points, CadCommand point
  methods, TangentObject already DVec3 (radius f64).
- snap: SnapResult.world DVec3; world_to_screen/try_pt DVec3.
- GripApply -> DVec3; grip find/state (GripEdit) f64; camera
  pick_on_plane_f64 / pick_on_target_plane_f64; scene paper_to_model_f64.
- update.rs click + mouse-move + grip-drag pipelines route precise DVec3
  to on_point/on_preview_wires/on_entity_pick; ortho/polar cast at the
  UcsXform boundary only.
- cmd_result dispatch, transform helpers, dimension/arc/circle/multileader
  apply_grip, circle/arc/ellipse/shapes draw commands, array modify.

Tree does NOT compile yet (~236 errors remain across modify/annotate/
insert command files + entity apply_grip impls); committed at user request
to checkpoint progress.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-06-24 03:32:11 +03:00
commit 3da71698b1
18 changed files with 626 additions and 587 deletions

View file

@ -3594,6 +3594,11 @@ impl Scene {
/// geometry of the currently active viewport. Returns the input unchanged
/// when there is no active viewport.
pub fn paper_to_model(&self, paper_pt: glam::Vec3) -> glam::Vec3 {
self.paper_to_model_f64(paper_pt.as_dvec3()).as_vec3()
}
/// f64 variant of [`paper_to_model`] — preserves UTM-scale precision.
pub fn paper_to_model_f64(&self, paper_pt: glam::DVec3) -> glam::DVec3 {
let vp_handle = match self.active_viewport {
Some(h) => h,
None => return paper_pt,
@ -3602,16 +3607,15 @@ impl Scene {
Some(acadrust::EntityType::Viewport(vp)) => vp,
_ => return paper_pt,
};
let scale =
vp_effective_scale(vp.custom_scale, vp.view_height, vp.height) as f32;
let scale = vp_effective_scale(vp.custom_scale, vp.view_height, vp.height);
if scale.abs() < 1e-9 {
return paper_pt;
}
let tx = vp.view_target.x as f32;
let ty = vp.view_target.y as f32;
let pcx = vp.center.x as f32;
let pcy = vp.center.y as f32;
glam::Vec3::new(
let tx = vp.view_target.x;
let ty = vp.view_target.y;
let pcx = vp.center.x;
let pcy = vp.center.y;
glam::DVec3::new(
(paper_pt.x - pcx) / scale + tx,
(paper_pt.y - pcy) / scale + ty,
paper_pt.z,

View file

@ -1,7 +1,7 @@
// Shared value types used by the dispatch and grip systems.
use acadrust::types::{Color as AcadColor, LineWeight};
use glam::Vec3;
use glam::{DVec3, Vec3};
/// The kind of value held by a property row.
#[derive(Clone, Debug, PartialEq)]
@ -96,9 +96,9 @@ pub struct GripDef {
#[derive(Clone, Debug)]
pub enum GripApply {
/// Move a specific vertex to this absolute world position.
Absolute(Vec3),
Absolute(DVec3),
/// Translate the whole object by this delta vector.
Translate(Vec3),
Translate(DVec3),
}
/// One entry in the hover-popup menu that opens when the cursor dwells

View file

@ -1,7 +1,7 @@
//! OpenCADStudio-style grip editing.
use acadrust::Handle;
use glam::Vec3;
use glam::{DVec3, Vec3};
use iced::{Point, Rectangle};
use crate::scene::model::object::{GripDef, GripShape};
@ -23,9 +23,9 @@ pub struct GripEdit {
/// `true` → midpoint / translate grip; `false` → endpoint / absolute grip.
pub is_translate: bool,
/// World-space position of the grip when the drag started (ortho/polar base).
pub origin_world: Vec3,
pub origin_world: DVec3,
/// Last world-space cursor position (needed for incremental delta on translate drags).
pub last_world: Vec3,
pub last_world: DVec3,
}
// ── Screen-space helpers ───────────────────────────────────────────────────
@ -83,9 +83,9 @@ pub fn find_hit_grip_paper(
half_w: f32,
half_h: f32,
bounds: Rectangle,
) -> Option<(usize, bool, Vec3)> {
) -> Option<(usize, bool, DVec3)> {
let mut best_dist = GRIP_THRESHOLD_PX;
let mut best: Option<(usize, bool, Vec3)> = None;
let mut best: Option<(usize, bool, DVec3)> = None;
for g in grips {
let screen = Point::new(
@ -97,7 +97,7 @@ pub fn find_hit_grip_paper(
let d = (dx * dx + dy * dy).sqrt();
if d < best_dist {
best_dist = d;
best = Some((g.id, g.is_midpoint, g.world.as_vec3()));
best = Some((g.id, g.is_midpoint, g.world));
}
}
best
@ -110,9 +110,9 @@ pub fn find_hit_grip(
grips: &[GripDef],
camera: &crate::scene::view::camera::Camera,
bounds: Rectangle,
) -> Option<(usize, bool, Vec3)> {
) -> Option<(usize, bool, DVec3)> {
let mut best_dist = GRIP_THRESHOLD_PX;
let mut best: Option<(usize, bool, Vec3)> = None;
let mut best: Option<(usize, bool, DVec3)> = None;
for g in grips {
let Some(screen) = camera.project_f64(g.world, bounds) else {
@ -124,7 +124,7 @@ pub fn find_hit_grip(
let d = (dx * dx + dy * dy).sqrt();
if d < best_dist {
best_dist = d;
best = Some((g.id, g.is_midpoint, g.world.as_vec3()));
best = Some((g.id, g.is_midpoint, g.world));
}
}
best

View file

@ -233,9 +233,24 @@ impl Camera {
}
pub fn pick_on_target_plane(&self, screen: Point, bounds: Rectangle) -> Vec3 {
self.pick_on_target_plane_f64(screen, bounds).as_vec3()
}
/// f64 variant of [`pick_on_plane`] — keeps the cursor precise at UTM-scale.
pub fn pick_on_plane_f64(
&self,
screen: Point,
bounds: Rectangle,
plane_normal: Vec3,
plane_point: glam::DVec3,
) -> glam::DVec3 {
self.unproject_on_plane_f64(screen, bounds, plane_normal, plane_point)
}
/// f64 variant of [`pick_on_target_plane`].
pub fn pick_on_target_plane_f64(&self, screen: Point, bounds: Rectangle) -> glam::DVec3 {
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()
}

View file

@ -1,14 +1,14 @@
use acadrust::types::{Transform, Vector3};
use glam::Vec3;
use glam::DVec3;
use crate::command::EntityTransform;
#[inline]
fn to_v3(v: Vec3) -> Vector3 {
Vector3::new(v.x as f64, v.y as f64, v.z as f64)
fn to_v3(v: DVec3) -> Vector3 {
Vector3::new(v.x, v.y, v.z)
}
pub fn apply_standard_transform<T>(entity: &mut T, center: Vec3, angle_rad: f32)
pub fn apply_standard_transform<T>(entity: &mut T, center: DVec3, angle_rad: f32)
where
T: acadrust::Entity,
{
@ -19,7 +19,7 @@ where
entity.apply_transform(&t);
}
pub fn apply_standard_scale<T>(entity: &mut T, center: Vec3, factor: f32)
pub fn apply_standard_scale<T>(entity: &mut T, center: DVec3, factor: f32)
where
T: acadrust::Entity,
{
@ -31,7 +31,7 @@ where
pub fn apply_standard_entity_transform<T, F>(entity: &mut T, t: &EntityTransform, mirror: F)
where
T: acadrust::Entity,
F: FnOnce(&mut T, Vec3, Vec3),
F: FnOnce(&mut T, DVec3, DVec3),
{
match t {
EntityTransform::Translate(d) => entity.translate(to_v3(*d)),
@ -47,7 +47,7 @@ where
/// `Transform`, for delegating MIRROR to acadrust's entity-aware
/// `apply_transform` paths (which handle direction flags, stored-angle
/// conventions and bulges themselves). Degenerate line → identity.
pub fn reflection_about_xy_line(p1: Vec3, p2: Vec3) -> acadrust::types::Transform {
pub fn reflection_about_xy_line(p1: DVec3, p2: DVec3) -> acadrust::types::Transform {
use acadrust::types::{Matrix4, Transform};
let dx = (p2.x - p1.x) as f64;
let dy = (p2.y - p1.y) as f64;
@ -63,7 +63,7 @@ pub fn reflection_about_xy_line(p1: Vec3, p2: Vec3) -> acadrust::types::Transfor
Transform::from_matrix(m)
}
pub fn reflect_xy_point(x: &mut f64, y: &mut f64, p1: Vec3, p2: Vec3) {
pub fn reflect_xy_point(x: &mut f64, y: &mut f64, p1: DVec3, p2: DVec3) {
let ax = (p2.x - p1.x) as f64;
let ay = (p2.y - p1.y) as f64;
let len2 = ax * ax + ay * ay;
@ -79,7 +79,7 @@ pub fn reflect_xy_point(x: &mut f64, y: &mut f64, p1: Vec3, p2: Vec3) {
*y = p1.y as f64 + my;
}
pub fn mirror_xy_line(line: &mut acadrust::entities::Line, p1: Vec3, p2: Vec3) {
pub fn mirror_xy_line(line: &mut acadrust::entities::Line, p1: DVec3, p2: DVec3) {
reflect_xy_point(&mut line.start.x, &mut line.start.y, p1, p2);
reflect_xy_point(&mut line.end.x, &mut line.end.y, p1, p2);
}
@ -171,8 +171,8 @@ mod mirror_delegation_tests {
// Mirror across the vertical line x = 5.
h.apply_transform(&EntityTransform::Mirror {
p1: Vec3::new(5.0, 0.0, 0.0),
p2: Vec3::new(5.0, 1.0, 0.0),
p1: DVec3::new(5.0, 0.0, 0.0),
p2: DVec3::new(5.0, 1.0, 0.0),
});
let edges = &h.paths[0].edges;