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![]),
|
object: RenderObject::Lines(vec![]),
|
||||||
snap_pts: vec![(snap, SnapHint::Node)],
|
snap_pts: vec![(snap, SnapHint::Node)],
|
||||||
tangent_geoms: vec![],
|
tangent_geoms: vec![],
|
||||||
key_vertices: vec![[wx, wy, wz]],
|
key_vertices: vec![],
|
||||||
fill_tris: 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 {
|
key_vertices: if pt.thickness.abs() > 1.0e-10 {
|
||||||
vec![[wx, wy, wz], [top.x, top.y, top.z]]
|
vec![[wx, wy, wz], [top.x, top.y, top.z]]
|
||||||
} else {
|
} else {
|
||||||
vec![[wx, wy, wz]]
|
// Snaps as a Node only, never as an endpoint.
|
||||||
|
vec![]
|
||||||
},
|
},
|
||||||
fill_tris: vec![],
|
fill_tris: vec![],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -744,6 +744,11 @@ impl WireModel {
|
|||||||
pub fn is_display_only(&self) -> bool {
|
pub fn is_display_only(&self) -> bool {
|
||||||
self.snap_only && self.display_visible
|
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.
|
/// Sentinel AABB that never rejects any snap query.
|
||||||
pub const UNBOUNDED_AABB: [f32; 4] = [
|
pub const UNBOUNDED_AABB: [f32; 4] = [
|
||||||
f32::NEG_INFINITY,
|
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) {
|
for start in 0..wire.points.len().saturating_sub(1) {
|
||||||
let Some(aabb) = points_aabb3([
|
let Some(aabb) = points_aabb3([
|
||||||
wire_point(wire, start),
|
wire_point(wire, start),
|
||||||
|
|||||||
+19
-5
@@ -1346,10 +1346,17 @@ impl Snapper {
|
|||||||
const MAX_PAIRWISE_POINTS: usize = 3_000;
|
const MAX_PAIRWISE_POINTS: usize = 3_000;
|
||||||
let mut in_range_pts = 0usize;
|
let mut in_range_pts = 0usize;
|
||||||
let mut in_range_wires = InRangeWires::new();
|
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();
|
let unindexed = wires.segments().is_none();
|
||||||
if unindexed {
|
if unindexed {
|
||||||
for w in wires.iter() {
|
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_pts += w.points.len();
|
||||||
in_range_wires.push(w);
|
in_range_wires.push(w);
|
||||||
}
|
}
|
||||||
@@ -1361,7 +1368,11 @@ impl Snapper {
|
|||||||
// Early out: if no unindexed wires overlap the cursor aperture and
|
// Early out: if no unindexed wires overlap the cursor aperture and
|
||||||
// no persistent tracking points exist, discrete/continuous object snaps
|
// no persistent tracking points exist, discrete/continuous object snaps
|
||||||
// cannot hit anything. Avoid running all subsequent pass setups and loops.
|
// 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;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1460,7 +1471,7 @@ impl Snapper {
|
|||||||
try_snap_hint(world, hint, wire_source(wire));
|
try_snap_hint(world, hint, wire_source(wire));
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
let src = wire_source(wire);
|
||||||
for &(world, hint) in &wire.snap_pts {
|
for &(world, hint) in &wire.snap_pts {
|
||||||
try_snap_hint(world, hint, src);
|
try_snap_hint(world, hint, src);
|
||||||
@@ -1489,7 +1500,10 @@ impl Snapper {
|
|||||||
// Tessellated open curves have no key-vertex set. Their only
|
// Tessellated open curves have no key-vertex set. Their only
|
||||||
// endpoints are first/last, so testing candidate wires remains
|
// endpoints are first/last, so testing candidate wires remains
|
||||||
// O(local wires), independent of tessellation density.
|
// 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
|
let closed = wire
|
||||||
.snap_pts
|
.snap_pts
|
||||||
.iter()
|
.iter()
|
||||||
@@ -1516,7 +1530,7 @@ impl Snapper {
|
|||||||
for &point in &wire.key_vertices {
|
for &point in &wire.key_vertices {
|
||||||
try_pt(DVec3::from_array(point), SnapType::Endpoint, src, None);
|
try_pt(DVec3::from_array(point), SnapType::Endpoint, src, None);
|
||||||
}
|
}
|
||||||
} else {
|
} else if !wire.is_node_marker() {
|
||||||
let closed = wire
|
let closed = wire
|
||||||
.snap_pts
|
.snap_pts
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
Reference in new issue
Block a user