forked from 1b-agent/opencadstudio-upstream
fix(osnap): a POINT snaps as a Node only
A point carried a key vertex, so Endpoint (which outranks Node) claimed it, and an absolute-size glyph's strokes were indexed for Nearest and Intersection. Points now offer only their Node, in the indexed and the unindexed snap paths alike. Fixes #1570 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d6dfd0d46d
commit
fd01a36ad3
4 files changed
+28
-8
No files matched your search
@@ -51,7 +51,7 @@ fn point_render(pt: &Point, pdmode: i16, s: f64) -> RenderEntity {
|
||||
object: RenderObject::Lines(vec![]),
|
||||
snap_pts: vec![(snap, SnapHint::Node)],
|
||||
tangent_geoms: vec![],
|
||||
key_vertices: vec![[wx, wy, wz]],
|
||||
key_vertices: vec![],
|
||||
fill_tris: vec![],
|
||||
};
|
||||
}
|
||||
@@ -63,7 +63,8 @@ fn point_render(pt: &Point, pdmode: i16, s: f64) -> RenderEntity {
|
||||
key_vertices: if pt.thickness.abs() > 1.0e-10 {
|
||||
vec![[wx, wy, wz], [top.x, top.y, top.z]]
|
||||
} else {
|
||||
vec![[wx, wy, wz]]
|
||||
// Snaps as a Node only, never as an endpoint.
|
||||
vec![]
|
||||
},
|
||||
fill_tris: vec![],
|
||||
}
|
||||
|
||||
@@ -744,6 +744,11 @@ impl WireModel {
|
||||
pub fn is_display_only(&self) -> bool {
|
||||
self.snap_only && self.display_visible
|
||||
}
|
||||
/// A POINT's wire: the Node is its only snap; the glyph strokes are drawn
|
||||
/// but offer no endpoints or segments to snap to.
|
||||
pub fn is_node_marker(&self) -> bool {
|
||||
self.key_vertices.is_empty() && matches!(self.snap_pts.as_slice(), [(_, SnapHint::Node)])
|
||||
}
|
||||
/// Sentinel AABB that never rejects any snap query.
|
||||
pub const UNBOUNDED_AABB: [f32; 4] = [
|
||||
f32::NEG_INFINITY,
|
||||
|
||||
@@ -699,7 +699,7 @@ fn append_wire_index_entries(wire_idx: u32, wire: &WireModel, batch: &mut WireIn
|
||||
);
|
||||
}
|
||||
|
||||
if wire.point_marker.is_none() {
|
||||
if wire.point_marker.is_none() && !wire.is_node_marker() {
|
||||
for start in 0..wire.points.len().saturating_sub(1) {
|
||||
let Some(aabb) = points_aabb3([
|
||||
wire_point(wire, start),
|
||||
|
||||
+19
-5
@@ -1346,10 +1346,17 @@ impl Snapper {
|
||||
const MAX_PAIRWISE_POINTS: usize = 3_000;
|
||||
let mut in_range_pts = 0usize;
|
||||
let mut in_range_wires = InRangeWires::new();
|
||||
// POINT wires offer only their Node; their glyph strokes stay out of
|
||||
// every geometric snap below.
|
||||
let mut in_range_nodes: Vec<&WireModel> = Vec::new();
|
||||
let unindexed = wires.segments().is_none();
|
||||
if unindexed {
|
||||
for w in wires.iter() {
|
||||
if !w.is_display_only() && wire_in_range(w) {
|
||||
if w.is_node_marker() {
|
||||
if wire_in_range(w) {
|
||||
in_range_nodes.push(w);
|
||||
}
|
||||
} else if !w.is_display_only() && wire_in_range(w) {
|
||||
in_range_pts += w.points.len();
|
||||
in_range_wires.push(w);
|
||||
}
|
||||
@@ -1361,7 +1368,11 @@ impl Snapper {
|
||||
// Early out: if no unindexed wires overlap the cursor aperture and
|
||||
// no persistent tracking points exist, discrete/continuous object snaps
|
||||
// cannot hit anything. Avoid running all subsequent pass setups and loops.
|
||||
if unindexed && in_range_wires.is_empty() && self.tracking_points.is_empty() {
|
||||
if unindexed
|
||||
&& in_range_wires.is_empty()
|
||||
&& in_range_nodes.is_empty()
|
||||
&& self.tracking_points.is_empty()
|
||||
{
|
||||
return best;
|
||||
}
|
||||
|
||||
@@ -1460,7 +1471,7 @@ impl Snapper {
|
||||
try_snap_hint(world, hint, wire_source(wire));
|
||||
}
|
||||
} else {
|
||||
for wire in in_range_wires.iter() {
|
||||
for wire in in_range_wires.iter().chain(in_range_nodes.iter().copied()) {
|
||||
let src = wire_source(wire);
|
||||
for &(world, hint) in &wire.snap_pts {
|
||||
try_snap_hint(world, hint, src);
|
||||
@@ -1489,7 +1500,10 @@ impl Snapper {
|
||||
// Tessellated open curves have no key-vertex set. Their only
|
||||
// endpoints are first/last, so testing candidate wires remains
|
||||
// O(local wires), independent of tessellation density.
|
||||
for wire in wires.iter().filter(|wire| wire.key_vertices.is_empty()) {
|
||||
for wire in wires
|
||||
.iter()
|
||||
.filter(|wire| wire.key_vertices.is_empty() && !wire.is_node_marker())
|
||||
{
|
||||
let closed = wire
|
||||
.snap_pts
|
||||
.iter()
|
||||
@@ -1516,7 +1530,7 @@ impl Snapper {
|
||||
for &point in &wire.key_vertices {
|
||||
try_pt(DVec3::from_array(point), SnapType::Endpoint, src, None);
|
||||
}
|
||||
} else {
|
||||
} else if !wire.is_node_marker() {
|
||||
let closed = wire
|
||||
.snap_pts
|
||||
.iter()
|
||||
|
||||
Reference in new issue
Block a user