Break polylines at picked segment parameters preserving bulges and widths

This commit is contained in:
ramox81 committed 2026-09-11 14:15:42 +03:00
1 parent 8ffd287c33
commit 9f37c0eb39
1 file changed
+34 -58
+34 -58
View File
@@ -41,10 +41,7 @@ pub fn break_entity(entity: &EntityType, p1: DVec3, p2: DVec3) -> Option<Vec<Ent
EntityType::Line(line) => Some(break_line(line, p1, p2)), EntityType::Line(line) => Some(break_line(line, p1, p2)),
EntityType::Arc(arc) => Some(break_arc(arc, p1, p2)), EntityType::Arc(arc) => Some(break_arc(arc, p1, p2)),
EntityType::Circle(c) => Some(break_circle(c, p1, p2)), EntityType::Circle(c) => Some(break_circle(c, p1, p2)),
EntityType::LwPolyline(p) => { EntityType::LwPolyline(p) => Some(break_lwpolyline(p, p1, p2)),
let p = crate::entities::curve::lwpolyline_world_xy(p)?;
Some(break_lwpolyline(&p, p1, p2))
}
EntityType::Ellipse(e) => Some(break_ellipse(e, p1, p2)), EntityType::Ellipse(e) => Some(break_ellipse(e, p1, p2)),
EntityType::Spline(s) => Some(break_spline(s, p1, p2)), EntityType::Spline(s) => Some(break_spline(s, p1, p2)),
_ => None, _ => None,
@@ -133,48 +130,41 @@ fn break_circle(circle: &acadrust::entities::Circle, p1: DVec3, p2: DVec3) -> Ve
}).collect() }).collect()
} }
fn break_lwpolyline(p: &LwPolyline, p1: DVec3, p2: DVec3) -> Vec<EntityType> { fn break_lwpolyline(p: &LwPolyline, p1: DVec3, p2: DVec3) -> Vec<EntityType> {
// For LwPolyline, find the nearest vertex indices for p1 and p2, let unchanged = || vec![EntityType::LwPolyline(p.clone())];
// then split into two polylines at those vertices. let Some(curve) = crate::entities::curve::lwpolyline_curve(p) else { return unchanged(); };
let n = p.vertices.len(); let Some(spans) = picked_spans(&curve, p1, p2) else { return unchanged(); };
if n < 2 { let cadkernel::geom2d::Curve::Polyline(geometry) = &curve.curve else { unreachable!() };
return vec![EntityType::LwPolyline(p.clone())]; let mut result = Vec::with_capacity(spans.len());
} for [from, to] in spans {
let Some(range) = geometry.ranged(from, to) else { return unchanged(); };
let t1 = nearest_pline_param(p, p1); let mut fragment = p.clone();
let t2 = nearest_pline_param(p, p2); fragment.common.handle = Handle::NULL;
let (ta, tb) = if t1 <= t2 { (t1, t2) } else { (t2, t1) }; fragment.is_closed = false;
fragment.vertices.clear();
// Build two polylines: [0..ta] and [tb..end] for (index, vertex) in range.polyline.vertices.iter().enumerate() {
let idx_a = ta.min(n - 1); // The final vertex has no outgoing segment. Keep its source metadata
let idx_b = tb.min(n - 1); // when it is an original endpoint, otherwise the preceding segment's.
let segment = &range.segments[index.min(range.segments.len() - 1)];
let mut result = Vec::new(); let last = index == range.segments.len();
let source = if last && segment.to == 1.0 {
// First piece (segment.source_index + 1) % p.vertices.len()
if idx_a > 0 { } else { segment.source_index };
let mut first = p.clone(); let mut output = p.vertices[source].clone();
first.common.handle = Handle::NULL; output.location.x = vertex.position[0];
first.vertices = p.vertices[..=idx_a].to_vec(); output.location.y = vertex.position[1];
first.is_closed = false; if !last || segment.to != 1.0 {
result.push(EntityType::LwPolyline(first)); let original = &p.vertices[segment.source_index];
} let widths = segment.interpolate(original.start_width, original.end_width);
output.start_width = widths[0];
// Second piece output.end_width = widths[1];
if idx_b < n - 1 { output.bulge = if last { range.polyline.vertices[index - 1].bulge } else { vertex.bulge };
let mut second = p.clone(); }
second.common.handle = Handle::NULL; fragment.vertices.push(output);
second.vertices = p.vertices[idx_b..].to_vec(); }
second.is_closed = false; result.push(EntityType::LwPolyline(fragment));
result.push(EntityType::LwPolyline(second));
}
if result.is_empty() {
vec![EntityType::LwPolyline(p.clone())]
} else {
result
} }
result
} }
fn break_ellipse(ell: &EllipseEnt, p1: DVec3, p2: DVec3) -> Vec<EntityType> { fn break_ellipse(ell: &EllipseEnt, p1: DVec3, p2: DVec3) -> Vec<EntityType> {
let Some(curve) = crate::entities::curve::ellipse_curve(ell) else { let Some(curve) = crate::entities::curve::ellipse_curve(ell) else {
return vec![EntityType::Ellipse(ell.clone())]; return vec![EntityType::Ellipse(ell.clone())];
@@ -191,20 +181,6 @@ fn break_ellipse(ell: &EllipseEnt, p1: DVec3, p2: DVec3) -> Vec<EntityType> {
EntityType::Ellipse(result) EntityType::Ellipse(result)
}).collect() }).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 { fn world_to_dxf(v: DVec3) -> DVec3 {
// World = DXF (identity). // World = DXF (identity).
DVec3::new(v.x, v.y, v.z) DVec3::new(v.x, v.y, v.z)