From 9f37c0eb39e2c0f61a277e4d9a33f774713d0376 Mon Sep 17 00:00:00 2001 From: ramox81 <184937705+ramox81@users.noreply.github.com> Date: Fri, 11 Sep 2026 14:15:42 +0300 Subject: [PATCH] Break polylines at picked segment parameters preserving bulges and widths --- src/modules/draw/modify/break_cmd.rs | 92 ++++++++++------------------ 1 file changed, 34 insertions(+), 58 deletions(-) diff --git a/src/modules/draw/modify/break_cmd.rs b/src/modules/draw/modify/break_cmd.rs index 81954465d..8b45eb8de 100644 --- a/src/modules/draw/modify/break_cmd.rs +++ b/src/modules/draw/modify/break_cmd.rs @@ -41,10 +41,7 @@ pub fn break_entity(entity: &EntityType, p1: DVec3, p2: DVec3) -> Option Some(break_line(line, p1, p2)), EntityType::Arc(arc) => Some(break_arc(arc, p1, p2)), EntityType::Circle(c) => Some(break_circle(c, p1, p2)), - EntityType::LwPolyline(p) => { - let p = crate::entities::curve::lwpolyline_world_xy(p)?; - Some(break_lwpolyline(&p, p1, p2)) - } + EntityType::LwPolyline(p) => Some(break_lwpolyline(p, p1, p2)), EntityType::Ellipse(e) => Some(break_ellipse(e, p1, p2)), EntityType::Spline(s) => Some(break_spline(s, p1, p2)), _ => None, @@ -133,48 +130,41 @@ fn break_circle(circle: &acadrust::entities::Circle, p1: DVec3, p2: DVec3) -> Ve }).collect() } fn break_lwpolyline(p: &LwPolyline, p1: DVec3, p2: DVec3) -> Vec { - // For LwPolyline, find the nearest vertex indices for p1 and p2, - // then split into two polylines at those vertices. - let n = p.vertices.len(); - if n < 2 { - return vec![EntityType::LwPolyline(p.clone())]; - } - - let t1 = nearest_pline_param(p, p1); - let t2 = nearest_pline_param(p, p2); - let (ta, tb) = if t1 <= t2 { (t1, t2) } else { (t2, t1) }; - - // Build two polylines: [0..ta] and [tb..end] - let idx_a = ta.min(n - 1); - let idx_b = tb.min(n - 1); - - let mut result = Vec::new(); - - // First piece - if idx_a > 0 { - let mut first = p.clone(); - first.common.handle = Handle::NULL; - first.vertices = p.vertices[..=idx_a].to_vec(); - first.is_closed = false; - result.push(EntityType::LwPolyline(first)); - } - - // Second piece - if idx_b < n - 1 { - let mut second = p.clone(); - second.common.handle = Handle::NULL; - second.vertices = p.vertices[idx_b..].to_vec(); - second.is_closed = false; - result.push(EntityType::LwPolyline(second)); - } - - if result.is_empty() { - vec![EntityType::LwPolyline(p.clone())] - } else { - result + let unchanged = || vec![EntityType::LwPolyline(p.clone())]; + let Some(curve) = crate::entities::curve::lwpolyline_curve(p) else { return unchanged(); }; + let Some(spans) = picked_spans(&curve, p1, p2) else { return unchanged(); }; + let cadkernel::geom2d::Curve::Polyline(geometry) = &curve.curve else { unreachable!() }; + let mut result = Vec::with_capacity(spans.len()); + for [from, to] in spans { + let Some(range) = geometry.ranged(from, to) else { return unchanged(); }; + let mut fragment = p.clone(); + fragment.common.handle = Handle::NULL; + fragment.is_closed = false; + fragment.vertices.clear(); + for (index, vertex) in range.polyline.vertices.iter().enumerate() { + // The final vertex has no outgoing segment. Keep its source metadata + // when it is an original endpoint, otherwise the preceding segment's. + let segment = &range.segments[index.min(range.segments.len() - 1)]; + let last = index == range.segments.len(); + let source = if last && segment.to == 1.0 { + (segment.source_index + 1) % p.vertices.len() + } else { segment.source_index }; + let mut output = p.vertices[source].clone(); + output.location.x = vertex.position[0]; + output.location.y = vertex.position[1]; + if !last || segment.to != 1.0 { + let original = &p.vertices[segment.source_index]; + let widths = segment.interpolate(original.start_width, original.end_width); + output.start_width = widths[0]; + output.end_width = widths[1]; + output.bulge = if last { range.polyline.vertices[index - 1].bulge } else { vertex.bulge }; + } + fragment.vertices.push(output); + } + result.push(EntityType::LwPolyline(fragment)); } + result } - fn break_ellipse(ell: &EllipseEnt, p1: DVec3, p2: DVec3) -> Vec { let Some(curve) = crate::entities::curve::ellipse_curve(ell) else { return vec![EntityType::Ellipse(ell.clone())]; @@ -191,20 +181,6 @@ fn break_ellipse(ell: &EllipseEnt, p1: DVec3, p2: DVec3) -> Vec { EntityType::Ellipse(result) }).collect() } -/// Find the index of the polyline vertex closest to `pt`. -fn nearest_pline_param(p: &LwPolyline, pt: DVec3) -> usize { - p.vertices - .iter() - .enumerate() - .min_by_key(|(_, v)| { - let dx = v.location.x - pt.x; - let dy = v.location.y - pt.y; - ((dx * dx + dy * dy) * 1e6) as i64 - }) - .map(|(i, _)| i) - .unwrap_or(0) -} - fn world_to_dxf(v: DVec3) -> DVec3 { // World = DXF (identity). DVec3::new(v.x, v.y, v.z)