forked from 1b-agent/opencadstudio-upstream
fix(snap): f64-precise computed snaps + UTM-tolerant cull + f64 previews
Computed snaps (midpoint/nearest/perpendicular/intersection/extension/ apparent/grid) reconstructed the snap point from f32 `wire.points` (absolute high half), so at UTM-scale they landed ~0.5 m off the geometry while endpoint/center (sourced from f64 key_vertices/snap_pts) stayed correct. Now they rebuild each vertex from points + points_low (the double-single low residual) and run the geometry in f64: - snap() cursor + geometry helpers (nearest_on_segment, perp_foot, seg_intersect_xy, extension_snap) are DVec3; wp_f64() reconstructs each vertex; midpoint uses f64 key_vertices directly (was downcast to f32). - snap callers pass the precise DVec3 cursor. Snap capture at deep zoom: the wire-AABB cull compared a sub-millimetre snap radius against f32 bounds quantized by ~1 ulp (~0.7 m at 5.7e6), wrongly excluding wires under the cursor. Pad the cull by the bound's own f32 quantization so it never rejects an in-range wire. Rubber-band previews (line/circle/arc/ellipse/rect/polygon) built f32 absolute points with an empty points_low, so the wire pass (relative-to- eye) drew them ~0.5 m off the cursor at UTM. Add WireModel::solid_f64, which splits f64 points into the double-single high/low pair; previews now track the cursor exactly. Known: snapping at extreme zoom on large drawings spikes CPU (per-move snap cost) — deferred. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
4f7a98a2ad
commit
bca8958025
8 files changed
+166
-158
No files matched your search
@@ -79,17 +79,17 @@ fn arc_preview(center: DVec3, radius: f64, start_angle: f64, end_angle: f64) ->
|
||||
}
|
||||
let span = (ea - start_angle).min(TAU);
|
||||
let segs = ((span / TAU) * 64.0).ceil().max(4.0) as u32;
|
||||
let pts: Vec<[f32; 3]> = (0..=segs)
|
||||
let pts: Vec<[f64; 3]> = (0..=segs)
|
||||
.map(|i| {
|
||||
let a = start_angle + span * (i as f64 / segs as f64);
|
||||
[
|
||||
(center.x + radius * a.cos()) as f32,
|
||||
(center.y + radius * a.sin()) as f32,
|
||||
center.z as f32,
|
||||
center.x + radius * a.cos(),
|
||||
center.y + radius * a.sin(),
|
||||
center.z,
|
||||
]
|
||||
})
|
||||
.collect();
|
||||
WireModel::solid("rubber_band".into(), pts, WireModel::CYAN, false)
|
||||
WireModel::solid_f64("rubber_band".into(), pts, WireModel::CYAN, false)
|
||||
}
|
||||
|
||||
fn make_arc(center: DVec3, radius: f64, start_angle: f64, end_angle: f64) -> EntityType {
|
||||
@@ -116,12 +116,9 @@ fn rot_delta(center: DVec3, prev: DVec3, curr: DVec3) -> f64 {
|
||||
const DIR_TOL: f64 = 0.1745; // ~10°
|
||||
|
||||
fn line_wire(a: DVec3, b: DVec3) -> WireModel {
|
||||
WireModel::solid(
|
||||
WireModel::solid_f64(
|
||||
"rubber_band".into(),
|
||||
vec![
|
||||
[a.x as f32, a.y as f32, a.z as f32],
|
||||
[b.x as f32, b.y as f32, b.z as f32],
|
||||
],
|
||||
vec![[a.x, a.y, a.z], [b.x, b.y, b.z]],
|
||||
WireModel::CYAN,
|
||||
false,
|
||||
)
|
||||
@@ -386,13 +383,9 @@ impl CadCommand for Arc3PCommand {
|
||||
};
|
||||
Some(arc_preview(center, radius, sa, ea))
|
||||
} else {
|
||||
Some(WireModel::solid(
|
||||
Some(WireModel::solid_f64(
|
||||
"rubber_band".into(),
|
||||
vec![
|
||||
[p1.x as f32, p1.y as f32, p1.z as f32],
|
||||
[p2.x as f32, p2.y as f32, p2.z as f32],
|
||||
[pt.x as f32, pt.y as f32, pt.z as f32],
|
||||
],
|
||||
vec![[p1.x, p1.y, p1.z], [p2.x, p2.y, p2.z], [pt.x, pt.y, pt.z]],
|
||||
WireModel::CYAN,
|
||||
false,
|
||||
))
|
||||
|
||||
@@ -60,20 +60,20 @@ pub const ICON: IconKind = ICON_CR;
|
||||
|
||||
fn circle_wire(center: DVec3, radius: f64) -> WireModel {
|
||||
let segs = 64u32;
|
||||
let mut pts: Vec<[f32; 3]> = (0..=segs)
|
||||
let mut pts: Vec<[f64; 3]> = (0..=segs)
|
||||
.map(|i| {
|
||||
let a = (i as f64) * TAU / segs as f64;
|
||||
[
|
||||
(center.x + radius * a.cos()) as f32,
|
||||
(center.y + radius * a.sin()) as f32,
|
||||
center.z as f32,
|
||||
center.x + radius * a.cos(),
|
||||
center.y + radius * a.sin(),
|
||||
center.z,
|
||||
]
|
||||
})
|
||||
.collect();
|
||||
if let Some(first) = pts.first().cloned() {
|
||||
pts.push(first);
|
||||
}
|
||||
WireModel::solid("rubber_band".into(), pts, WireModel::CYAN, false)
|
||||
WireModel::solid_f64("rubber_band".into(), pts, WireModel::CYAN, false)
|
||||
}
|
||||
|
||||
fn make_circle(center: DVec3, radius: f64) -> EntityType {
|
||||
@@ -404,13 +404,9 @@ impl CadCommand for Circle3PCommand {
|
||||
if let Some((center, radius)) = circumcircle(a, b, pt) {
|
||||
Some(circle_wire(center, radius))
|
||||
} else {
|
||||
Some(WireModel::solid(
|
||||
Some(WireModel::solid_f64(
|
||||
"rubber_band".into(),
|
||||
vec![
|
||||
[a.x as f32, a.y as f32, a.z as f32],
|
||||
[b.x as f32, b.y as f32, b.z as f32],
|
||||
[pt.x as f32, pt.y as f32, pt.z as f32],
|
||||
],
|
||||
vec![[a.x, a.y, a.z], [b.x, b.y, b.z], [pt.x, pt.y, pt.z]],
|
||||
WireModel::CYAN,
|
||||
false,
|
||||
))
|
||||
|
||||
@@ -66,14 +66,14 @@ fn ellipse_wire(
|
||||
let segs = 64u32;
|
||||
// Unwrap t_end so the arc goes counter-clockwise.
|
||||
let t_e = if t_end <= t_start { t_end + TAU } else { t_end };
|
||||
let pts: Vec<[f32; 3]> = (0..=segs)
|
||||
let pts: Vec<[f64; 3]> = (0..=segs)
|
||||
.map(|i| {
|
||||
let t = t_start + (t_e - t_start) * (i as f64 / segs as f64);
|
||||
let p = center + t.cos() * r_major * major_dir + t.sin() * r_major * ratio * v;
|
||||
[p.x as f32, p.y as f32, p.z as f32]
|
||||
[p.x, p.y, p.z]
|
||||
})
|
||||
.collect();
|
||||
WireModel::solid("rubber_band".into(), pts, WireModel::CYAN, false)
|
||||
WireModel::solid_f64("rubber_band".into(), pts, WireModel::CYAN, false)
|
||||
}
|
||||
|
||||
/// Convert a world point to the parametric angle on the ellipse.
|
||||
@@ -610,28 +610,12 @@ fn angle_from_point(center: DVec3, major: DVec3, ratio: f64, pt: DVec3) -> f64 {
|
||||
}
|
||||
|
||||
fn line_wire(from: DVec3, to: DVec3) -> WireModel {
|
||||
WireModel {
|
||||
name: "rubber_band".into(),
|
||||
points: vec![
|
||||
[from.x as f32, from.y as f32, from.z as f32],
|
||||
[to.x as f32, to.y as f32, to.z as f32],
|
||||
],
|
||||
points_low: Vec::new(),
|
||||
color: WireModel::CYAN,
|
||||
selected: false,
|
||||
pattern_length: 0.0,
|
||||
pattern: [0.0; 8],
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
aabb: WireModel::UNBOUNDED_AABB,
|
||||
plinegen: true,
|
||||
vp_scissor: None,
|
||||
fill_tris: vec![],
|
||||
fill_tris_low: Vec::new(),
|
||||
}
|
||||
WireModel::solid_f64(
|
||||
"rubber_band".into(),
|
||||
vec![[from.x, from.y, from.z], [to.x, to.y, to.z]],
|
||||
WireModel::CYAN,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -75,28 +75,12 @@ impl CadCommand for LineCommand {
|
||||
|
||||
fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> {
|
||||
let last = self.last?;
|
||||
Some(WireModel {
|
||||
name: "rubber_band".to_string(),
|
||||
points: vec![
|
||||
[last.x as f32, last.y as f32, last.z as f32],
|
||||
[pt.x as f32, pt.y as f32, pt.z as f32],
|
||||
],
|
||||
points_low: Vec::new(),
|
||||
color: WireModel::CYAN,
|
||||
selected: false,
|
||||
pattern_length: 0.0,
|
||||
pattern: [0.0; 8],
|
||||
line_weight_px: 1.0,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
aci: 0,
|
||||
key_vertices: vec![],
|
||||
aabb: WireModel::UNBOUNDED_AABB,
|
||||
plinegen: true,
|
||||
vp_scissor: None,
|
||||
fill_tris: vec![],
|
||||
fill_tris_low: Vec::new(),
|
||||
})
|
||||
Some(WireModel::solid_f64(
|
||||
"rubber_band".to_string(),
|
||||
vec![[last.x, last.y, last.z], [pt.x, pt.y, pt.z]],
|
||||
WireModel::CYAN,
|
||||
false,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,21 +94,18 @@ fn make_pline(xy_pairs: &[[f64; 2]]) -> EntityType {
|
||||
})
|
||||
}
|
||||
|
||||
fn wire_loop(pts: Vec<[f32; 3]>) -> WireModel {
|
||||
fn wire_loop(pts: Vec<[f64; 3]>) -> WireModel {
|
||||
let mut p = pts;
|
||||
if let Some(&first) = p.first() {
|
||||
p.push(first);
|
||||
}
|
||||
WireModel::solid("rubber_band".into(), p, WireModel::CYAN, false)
|
||||
WireModel::solid_f64("rubber_band".into(), p, WireModel::CYAN, false)
|
||||
}
|
||||
|
||||
fn wire_seg(a: DVec3, b: DVec3) -> WireModel {
|
||||
WireModel::solid(
|
||||
WireModel::solid_f64(
|
||||
"rubber_band".into(),
|
||||
vec![
|
||||
[a.x as f32, a.y as f32, a.z as f32],
|
||||
[b.x as f32, b.y as f32, b.z as f32],
|
||||
],
|
||||
vec![[a.x, a.y, a.z], [b.x, b.y, b.z]],
|
||||
WireModel::CYAN,
|
||||
false,
|
||||
)
|
||||
@@ -129,13 +126,13 @@ fn poly_verts_xy(center: DVec3, vertex_r: f64, sides: u32, start_angle: f64) ->
|
||||
}
|
||||
|
||||
fn poly_wire(center: DVec3, vertex_r: f64, sides: u32, start_angle: f64) -> WireModel {
|
||||
let pts: Vec<[f32; 3]> = (0..sides)
|
||||
let pts: Vec<[f64; 3]> = (0..sides)
|
||||
.map(|i| {
|
||||
let a = start_angle + (i as f64) * TAU / sides as f64;
|
||||
[
|
||||
(center.x + vertex_r * a.cos()) as f32,
|
||||
(center.y + vertex_r * a.sin()) as f32,
|
||||
center.z as f32,
|
||||
center.x + vertex_r * a.cos(),
|
||||
center.y + vertex_r * a.sin(),
|
||||
center.z,
|
||||
]
|
||||
})
|
||||
.collect();
|
||||
@@ -200,10 +197,10 @@ impl CadCommand for RectCommand {
|
||||
let a = self.a?;
|
||||
let c = ucs_box_corners(a, pt, self.ucs);
|
||||
Some(wire_loop(vec![
|
||||
[c[0].x as f32, c[0].y as f32, c[0].z as f32],
|
||||
[c[1].x as f32, c[1].y as f32, c[1].z as f32],
|
||||
[c[2].x as f32, c[2].y as f32, c[2].z as f32],
|
||||
[c[3].x as f32, c[3].y as f32, c[3].z as f32],
|
||||
[c[0].x, c[0].y, c[0].z],
|
||||
[c[1].x, c[1].y, c[1].z],
|
||||
[c[2].x, c[2].y, c[2].z],
|
||||
[c[3].x, c[3].y, c[3].z],
|
||||
]))
|
||||
}
|
||||
fn dyn_spec(&self) -> Option<crate::command::DynSpec> {
|
||||
@@ -300,10 +297,10 @@ impl CadCommand for RectRotCommand {
|
||||
let c = b + perp * h;
|
||||
let d = a + perp * h;
|
||||
Some(wire_loop(vec![
|
||||
[a.x as f32, a.y as f32, a.z as f32],
|
||||
[b.x as f32, b.y as f32, b.z as f32],
|
||||
[c.x as f32, c.y as f32, c.z as f32],
|
||||
[d.x as f32, d.y as f32, d.z as f32],
|
||||
[a.x, a.y, a.z],
|
||||
[b.x, b.y, b.z],
|
||||
[c.x, c.y, c.z],
|
||||
[d.x, d.y, d.z],
|
||||
]))
|
||||
}
|
||||
_ => None,
|
||||
@@ -390,10 +387,10 @@ impl CadCommand for RectCenCommand {
|
||||
let c = self.center?;
|
||||
let q = ucs_box_around_center(c, pt, self.ucs);
|
||||
Some(wire_loop(vec![
|
||||
[q[0].x as f32, q[0].y as f32, q[0].z as f32],
|
||||
[q[1].x as f32, q[1].y as f32, q[1].z as f32],
|
||||
[q[2].x as f32, q[2].y as f32, q[2].z as f32],
|
||||
[q[3].x as f32, q[3].y as f32, q[3].z as f32],
|
||||
[q[0].x, q[0].y, q[0].z],
|
||||
[q[1].x, q[1].y, q[1].z],
|
||||
[q[2].x, q[2].y, q[2].z],
|
||||
[q[3].x, q[3].y, q[3].z],
|
||||
]))
|
||||
}
|
||||
fn dyn_spec(&self) -> Option<crate::command::DynSpec> {
|
||||
|
||||
Reference in new issue
Block a user