forked from 1b-agent/opencadstudio-upstream
fix(trim): polyline vertices are not cutting edges; self-cuts only as an edge
#1567 made every vertex a trim limit, so a polyline no edge crosses lost the clicked segment and a trimmed piece stopped at the next corner instead of running on to the next cutting edge. Vertices are no cuts again, a polyline with no cut is left alone, and its own crossings cut it only when it is among the cutting edges. The crossing window's unused pick corner is dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3bf8906ed3
commit
09cd50ea62
1 file changed
+18
-35
@@ -1170,10 +1170,11 @@ fn lwpoly_segment_curve(
|
||||
/// polyline intersect.
|
||||
///
|
||||
/// Segment neighbours are deliberately excluded: their common vertex is a
|
||||
/// normal polyline vertex, not a self-intersection.
|
||||
fn lwpoly_self_cut_params(poly: &LwPolyline) -> Vec<f64> {
|
||||
/// normal polyline vertex, not a self-intersection. A polyline cuts itself
|
||||
/// only when it is one of the cutting edges.
|
||||
fn lwpoly_self_cut_params(poly: &LwPolyline, geos: &[Geo]) -> Vec<f64> {
|
||||
let n = poly.vertices.len();
|
||||
if n < 3 {
|
||||
if n < 3 || !geos.iter().any(|geo| geo.handle() == poly.common.handle) {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
@@ -1286,7 +1287,6 @@ fn trim_lwpolyline(poly: &LwPolyline, cx: f64, cy: f64, geos: &[Geo]) -> Option<
|
||||
poly.vertices[i % n].bulge
|
||||
};
|
||||
|
||||
// Boundary cuts as global params (segment index + local u).
|
||||
// External boundary cuts as global params (segment index + local u).
|
||||
let mut cuts: Vec<f64> = Vec::new();
|
||||
|
||||
@@ -1307,25 +1307,16 @@ fn trim_lwpolyline(poly: &LwPolyline, cx: f64, cy: f64, geos: &[Geo]) -> Option<
|
||||
}
|
||||
|
||||
// A polyline can also cut itself. These are intersections between
|
||||
// non-adjacent segments belonging to this same LwPolyline.
|
||||
cuts.extend(lwpoly_self_cut_params(poly));
|
||||
|
||||
// Every polyline vertex is also a valid trim boundary. This allows an entire
|
||||
// segment between two vertices to be removed even when no external entity
|
||||
// crosses that particular segment.
|
||||
if closed {
|
||||
for i in 0..seg_count {
|
||||
cuts.push(i as f64);
|
||||
}
|
||||
} else {
|
||||
// 0 and `total` are added explicitly below as the open polyline ends.
|
||||
for i in 1..seg_count {
|
||||
cuts.push(i as f64);
|
||||
}
|
||||
}
|
||||
// non-adjacent segments belonging to this same LwPolyline. Its vertices
|
||||
// are not cuts: the trimmed piece runs on past corners to the next
|
||||
// cutting edge, and a polyline no edge crosses is left alone.
|
||||
cuts.extend(lwpoly_self_cut_params(poly, geos));
|
||||
|
||||
cuts.sort_by(f64::total_cmp);
|
||||
cuts.dedup_by(|a, b| (*a - *b).abs() < 1e-6);
|
||||
if cuts.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Click param: nearest point on the polyline.
|
||||
let mut best = (f64::INFINITY, 0.0_f64);
|
||||
@@ -1430,7 +1421,11 @@ fn trim_lwpolyline(poly: &LwPolyline, cx: f64, cy: f64, geos: &[Geo]) -> Option<
|
||||
}
|
||||
}
|
||||
|
||||
Some(out)
|
||||
if out.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(out)
|
||||
}
|
||||
}
|
||||
|
||||
// ── Extend helpers ────────────────────────────────────────────────────────
|
||||
@@ -1764,9 +1759,6 @@ enum TrimMode {
|
||||
struct CrossingWindow {
|
||||
min: [f64; 2],
|
||||
max: [f64; 2],
|
||||
/// The first corner is the trim-side hint, matching the side from which
|
||||
/// the crossing window was dragged.
|
||||
pick: [f64; 2],
|
||||
}
|
||||
|
||||
fn segment_window_range(
|
||||
@@ -1828,7 +1820,7 @@ fn crossing_trim_lwpolyline(
|
||||
|
||||
// Compute the polyline's own non-adjacent segment intersections once.
|
||||
// These global parameters will later be mapped back into each source segment.
|
||||
let self_cuts = lwpoly_self_cut_params(poly);
|
||||
let self_cuts = lwpoly_self_cut_params(poly, geos);
|
||||
|
||||
for i in 0..seg_count {
|
||||
let a = vertex_xy(i);
|
||||
@@ -1859,7 +1851,6 @@ fn crossing_trim_lwpolyline(
|
||||
continue;
|
||||
};
|
||||
// External intersections on this segment.
|
||||
// External intersections on this segment.
|
||||
let mut cuts = polyline_seg_ts(a, b, bulge, handle, geos);
|
||||
|
||||
// Also include self-intersections belonging to this source segment.
|
||||
@@ -2923,11 +2914,7 @@ impl CadCommand for TrimCommand {
|
||||
if !matches!(self.mode, TrimMode::Pick) || fence.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
let window = window.map(|(min, max)| CrossingWindow {
|
||||
min,
|
||||
max,
|
||||
pick: fence[0],
|
||||
});
|
||||
let window = window.map(|(min, max)| CrossingWindow { min, max });
|
||||
let replacements = fence_pass(
|
||||
&self.all_entities,
|
||||
&self.geos,
|
||||
@@ -3307,7 +3294,6 @@ impl CadCommand for TrimCommand {
|
||||
let window = CrossingWindow {
|
||||
min: [p1[0].min(p2[0]), p1[1].min(p2[1])],
|
||||
max: [p1[0].max(p2[0]), p1[1].max(p2[1])],
|
||||
pick: p1,
|
||||
};
|
||||
self.fence_run(&rect, Some(window))
|
||||
}
|
||||
@@ -3339,7 +3325,6 @@ impl CadCommand for TrimCommand {
|
||||
let window = CrossingWindow {
|
||||
min: [p1[0].min(p2[0]), p1[1].min(p2[1])],
|
||||
max: [p1[0].max(p2[0]), p1[1].max(p2[1])],
|
||||
pick: p1,
|
||||
};
|
||||
out.extend(fence_result_preview(
|
||||
&self.all_entities,
|
||||
@@ -3773,7 +3758,6 @@ impl CadCommand for ExtendCommand {
|
||||
let window = CrossingWindow {
|
||||
min: [p1[0].min(p2[0]), p1[1].min(p2[1])],
|
||||
max: [p1[0].max(p2[0]), p1[1].max(p2[1])],
|
||||
pick: p1,
|
||||
};
|
||||
self.fence_run(&rect, Some(window))
|
||||
}
|
||||
@@ -3805,7 +3789,6 @@ impl CadCommand for ExtendCommand {
|
||||
let window = CrossingWindow {
|
||||
min: [p1[0].min(p2[0]), p1[1].min(p2[1])],
|
||||
max: [p1[0].max(p2[0]), p1[1].max(p2[1])],
|
||||
pick: p1,
|
||||
};
|
||||
out.extend(fence_result_preview(
|
||||
&self.all_entities,
|
||||
|
||||
Reference in new issue
Block a user