forked from 1b-agent/opencadstudio-upstream
feat(otrack): track the direction from the base point to an acquired corner
Once the first point of a segment is placed, OTRACK now offers an alignment along the line joining that base point and an acquired tracking point — the direction *between* them, not only the ortho/polar axes or the corner's own edges. Acquire an existing corner while drawing and the cursor locks onto the base→corner line, with typed-distance measured from the base point. Adds a base→corner ray per acquired point in otrack_snap, grouped with that corner so it never self-intersects with the corner's other rays. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
4164ef7111
commit
5f64a5eb1b
1 file changed
+77
+77
@@ -486,6 +486,24 @@ impl Snapper {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Ray from the command's base point toward this acquired corner:
|
||||||
|
// once the first point is placed, the cursor can lock onto the line
|
||||||
|
// joining that base point and an existing corner — the direction
|
||||||
|
// *between* them, not only the ortho/polar axes or the corner's own
|
||||||
|
// edges. Grouped with the corner so it never self-intersects with
|
||||||
|
// that corner's other rays (they meet only at the corner).
|
||||||
|
if let Some(bp) = last_point {
|
||||||
|
let d = tp - bp;
|
||||||
|
let len2 = d.x * d.x + d.y * d.y;
|
||||||
|
if len2 > 1e-12 {
|
||||||
|
let inv = 1.0 / len2.sqrt();
|
||||||
|
rays.push(Ray {
|
||||||
|
origin: bp,
|
||||||
|
dir: Vec3::new(d.x * inv, d.y * inv, 0.0),
|
||||||
|
group: gi,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// OTRACK rays come first; the auxiliary rays appended below (polar from
|
// OTRACK rays come first; the auxiliary rays appended below (polar from
|
||||||
// last_point, ortho axis from last_point) only participate in
|
// last_point, ortho axis from last_point) only participate in
|
||||||
@@ -1851,6 +1869,65 @@ mod ext_tests {
|
|||||||
assert_eq!(s.tracking_points.len(), 2);
|
assert_eq!(s.tracking_points.len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn base_to_corner_direction_is_tracked() {
|
||||||
|
let mut s = Snapper::default();
|
||||||
|
s.otrack_enabled = true;
|
||||||
|
s.osnap_radius_px = 10.0;
|
||||||
|
// An acquired corner of an existing line, and the base point (first
|
||||||
|
// point) of the line currently being drawn.
|
||||||
|
let corner = Vec3::new(4.0, 2.0, 0.0);
|
||||||
|
s.tracking_points.push(corner);
|
||||||
|
s.tracking_dirs.push(Vec::new());
|
||||||
|
let base = Vec3::new(0.0, 0.0, 0.0);
|
||||||
|
|
||||||
|
// A plain orthographic camera: world * 0.1 → NDC, eye at the origin.
|
||||||
|
let view_rot = Mat4::from_scale(Vec3::splat(0.1));
|
||||||
|
let eye = glam::DVec3::ZERO;
|
||||||
|
let bounds = Rectangle {
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
width: 1000.0,
|
||||||
|
height: 1000.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Cursor past the corner along base→corner (≈26.57°, not an ortho/polar
|
||||||
|
// axis), nudged a hair off the line — only the new base→corner ray can
|
||||||
|
// lock it.
|
||||||
|
let dir = (corner - base).normalize();
|
||||||
|
let perp = Vec3::new(-dir.y, dir.x, 0.0);
|
||||||
|
let cursor = base + dir * 6.0 + perp * 0.05;
|
||||||
|
|
||||||
|
let hit = s
|
||||||
|
.otrack_snap(
|
||||||
|
cursor,
|
||||||
|
view_rot,
|
||||||
|
eye,
|
||||||
|
bounds,
|
||||||
|
None,
|
||||||
|
Some(base),
|
||||||
|
false,
|
||||||
|
Mat4::IDENTITY,
|
||||||
|
)
|
||||||
|
.expect("base→corner alignment should lock");
|
||||||
|
// The aligned point lies on the base→corner line, and the reported base
|
||||||
|
// is the command's base point (so typed-distance runs from there).
|
||||||
|
let off = hit.aligned - base;
|
||||||
|
let cross = off.x * dir.y - off.y * dir.x;
|
||||||
|
assert!(
|
||||||
|
cross.abs() < 1e-3,
|
||||||
|
"aligned point off the base→corner line: {:?}",
|
||||||
|
hit.aligned
|
||||||
|
);
|
||||||
|
assert!((hit.base - base).length() < 1e-3, "ray base is not the command base");
|
||||||
|
|
||||||
|
// Without a base point the alignment does not exist (nothing to join).
|
||||||
|
let none = s.otrack_snap(
|
||||||
|
cursor, view_rot, eye, bounds, None, None, false, Mat4::IDENTITY,
|
||||||
|
);
|
||||||
|
assert!(none.is_none(), "no base point → no base→corner alignment");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tangent_points_are_perpendicular_to_the_radius() {
|
fn tangent_points_are_perpendicular_to_the_radius() {
|
||||||
let c = Vec3::new(0.0, 0.0, 0.0);
|
let c = Vec3::new(0.0, 0.0, 0.0);
|
||||||
|
|||||||
Reference in new issue
Block a user