diff --git a/src/modules/draw/modify/explode.rs b/src/modules/draw/modify/explode.rs index d0869edc7..b98d3b030 100644 --- a/src/modules/draw/modify/explode.rs +++ b/src/modules/draw/modify/explode.rs @@ -168,9 +168,11 @@ fn explode_polyline2d(p: &Polyline2D) -> Vec { common, start: Vector3::new(p0[0], p0[1], elevation), end: Vector3::new(p1[0], p1[1], elevation), + thickness: p.thickness, ..LineEnt::new() })); - } else if let Some(arc) = bulge_to_arc(p0, p1, v0.bulge, elevation, &p.common) { + } else if let Some(arc) = bulge_to_arc(p0, p1, v0.bulge, elevation, &p.common, p.thickness) + { result.push(arc); } } @@ -237,12 +239,13 @@ fn explode_lwpolyline(p: &LwPolyline) -> Vec { common, start: Vector3::new(p0[0], p0[1], elevation), end: Vector3::new(p1[0], p1[1], elevation), + thickness: p.thickness, ..LineEnt::new() }; result.push(EntityType::Line(line)); } else { // Arc segment from bulge - if let Some(arc) = bulge_to_arc(p0, p1, v0.bulge, elevation, &p.common) { + if let Some(arc) = bulge_to_arc(p0, p1, v0.bulge, elevation, &p.common, p.thickness) { result.push(arc); } } @@ -258,6 +261,7 @@ fn bulge_to_arc( bulge: f64, elevation: f64, common_src: &EntityCommon, + thickness: f64, ) -> Option { let ba = crate::entities::common::BulgeArc::from_bulge(p0, p1, bulge)?; @@ -280,6 +284,7 @@ fn bulge_to_arc( radius: ba.radius, start_angle, end_angle, + thickness, ..ArcEnt::new() }; Some(EntityType::Arc(arc)) @@ -1720,4 +1725,42 @@ mod tests { dim_line ); } + + // EXPLODE replaces the polyline with fresh Line/Arc entities; each piece + // must carry the source thickness instead of resetting to 0 (#916). + #[test] + fn explode_keeps_lwpolyline_thickness() { + use acadrust::entities::LwVertex; + use acadrust::types::Vector2; + + let mut pl = LwPolyline::new(); + // One straight span + one bulged span, so the result holds a Line and + // an Arc and both paths through the rebuild are covered. + let mut v1 = LwVertex::new(Vector2::new(0.0, 0.0)); + v1.bulge = 1.0; // semicircle to the next vertex + pl.vertices = vec![ + v1, + LwVertex::new(Vector2::new(10.0, 0.0)), + LwVertex::new(Vector2::new(20.0, 0.0)), + ]; + pl.thickness = 4.0; + + let pieces = explode_polyline_segments(&EntityType::LwPolyline(pl)); + assert!(pieces.len() >= 2, "expected a line and an arc piece"); + for piece in &pieces { + match piece { + EntityType::Line(l) => assert!( + (l.thickness - 4.0).abs() < 1e-12, + "exploded line must keep thickness, got {}", + l.thickness + ), + EntityType::Arc(a) => assert!( + (a.thickness - 4.0).abs() < 1e-12, + "exploded arc must keep thickness, got {}", + a.thickness + ), + other => panic!("unexpected piece type: {other:?}"), + } + } + } } diff --git a/src/modules/draw/modify/join.rs b/src/modules/draw/modify/join.rs index b2bf2eae7..32dc0411b 100644 --- a/src/modules/draw/modify/join.rs +++ b/src/modules/draw/modify/join.rs @@ -176,6 +176,9 @@ pub fn join_entities(entities: &[(Handle, &EntityType)]) -> Option<(Vec, } let handles: Vec = entities.iter().map(|(h, _)| *h).collect(); let common = entities[0].1.common().clone(); + // Thickness (DXF 39) lives on the entity, not in `common`, so the rebuilt + // chain must carry it too or a thick source joins into a flat result (#916). + let thickness = crate::scene::view::dispatch::entity_thickness(entities[0].1).unwrap_or(0.0); let (chain, closed) = stitch(segs)?; @@ -198,6 +201,7 @@ pub fn join_entities(entities: &[(Handle, &EntityType)]) -> Option<(Vec, line.common.handle = Handle::NULL; line.start = v3(verts.first().unwrap().0); line.end = v3(verts.last().unwrap().0); + line.thickness = thickness; return Some((handles, vec![EntityType::Line(line)])); } @@ -216,6 +220,7 @@ pub fn join_entities(entities: &[(Handle, &EntityType)]) -> Option<(Vec, pl.vertices = lw_verts; pl.is_closed = closed; pl.elevation = z0; + pl.thickness = thickness; return Some((handles, vec![EntityType::LwPolyline(pl)])); } @@ -284,6 +289,71 @@ fn stitch(mut segs: Vec) -> Option<(Vec, bool)> { Some((chain, closed)) } +#[cfg(test)] +mod join_tests { + use super::*; + use acadrust::entities::{Line as LineEnt, LwVertex}; + use acadrust::Handle; + + fn line(x0: f64, y0: f64, x1: f64, y1: f64, thickness: f64) -> EntityType { + let mut l = LineEnt::new(); + l.start = Vector3::new(x0, y0, 0.0); + l.end = Vector3::new(x1, y1, 0.0); + l.thickness = thickness; + EntityType::Line(l) + } + + fn lw_2pts(p0: (f64, f64), p1: (f64, f64), thickness: f64) -> EntityType { + let mut pl = acadrust::entities::LwPolyline::new(); + pl.vertices = vec![ + LwVertex::new(Vector2::new(p0.0, p0.1)), + LwVertex::new(Vector2::new(p1.0, p1.1)), + ]; + pl.thickness = thickness; + EntityType::LwPolyline(pl) + } + + // JOIN rebuilds the result entity; thickness must follow the chain's first + // entity (the same source `common` comes from), not reset to 0 (#916). + #[test] + fn join_keeps_source_thickness() { + let h1 = Handle::new(1); + let h2 = Handle::new(2); + let e1 = lw_2pts((0.0, 0.0), (10.0, 0.0), 2.5); + // A corner keeps the chain from collapsing back to a single Line. + let e2 = line(10.0, 0.0, 10.0, 10.0, 0.0); + let (removed, out) = join_entities(&[(h1, &e1), (h2, &e2)]).expect("chain joins"); + assert_eq!(removed.len(), 2); + let Some(EntityType::LwPolyline(pl)) = out.first() else { + panic!("expected joined lwpolyline"); + }; + assert!( + (pl.thickness - 2.5).abs() < 1e-12, + "joined polyline must keep source thickness, got {}", + pl.thickness + ); + } + + // A collinear straight run collapses to a single Line; its thickness must + // come from the first source entity too, not default to 0. + #[test] + fn collinear_join_keeps_thickness() { + let h1 = Handle::new(1); + let h2 = Handle::new(2); + let e1 = line(0.0, 0.0, 5.0, 0.0, 1.25); + let e2 = line(5.0, 0.0, 10.0, 0.0, 0.0); + let (_, out) = join_entities(&[(h1, &e1), (h2, &e2)]).expect("chain joins"); + let Some(EntityType::Line(l)) = out.first() else { + panic!("expected collapsed line"); + }; + assert!( + (l.thickness - 1.25).abs() < 1e-12, + "collapsed line must keep source thickness, got {}", + l.thickness + ); + } +} + /// True when every vertex lies on one straight line (within tolerance). fn is_collinear(verts: &[(DVec3, f64)]) -> bool { if verts.len() < 3 { diff --git a/src/modules/draw/modify/pedit.rs b/src/modules/draw/modify/pedit.rs index 63051e5d0..d3d999776 100644 --- a/src/modules/draw/modify/pedit.rs +++ b/src/modules/draw/modify/pedit.rs @@ -686,6 +686,7 @@ pub fn convert_to_polyline(entity: &EntityType) -> Option { match entity { EntityType::Line(l) => { pl.common = l.common.clone(); + pl.thickness = l.thickness; pl.vertices = vec![ LwVertex::new(Vector2::new(l.start.x, l.start.y)), LwVertex::new(Vector2::new(l.end.x, l.end.y)), @@ -693,6 +694,7 @@ pub fn convert_to_polyline(entity: &EntityType) -> Option { } EntityType::Arc(a) => { pl.common = a.common.clone(); + pl.thickness = a.thickness; let (sa, ea) = (a.start_angle, a.end_angle); let sweep = { let s = (ea - sa).rem_euclid(TAU); @@ -720,6 +722,47 @@ pub fn convert_to_polyline(entity: &EntityType) -> Option { Some(EntityType::LwPolyline(pl)) } +#[cfg(test)] +mod convert_tests { + use super::*; + + // PEDIT convert rebuilds the entity as a polyline; thickness must move + // onto the new entity instead of resetting to 0 (#916). + #[test] + fn convert_keeps_line_thickness() { + let mut l = acadrust::entities::Line::new(); + l.start = acadrust::types::Vector3::new(0.0, 0.0, 0.0); + l.end = acadrust::types::Vector3::new(10.0, 0.0, 0.0); + l.thickness = 3.5; + let Some(EntityType::LwPolyline(pl)) = convert_to_polyline(&EntityType::Line(l)) else { + panic!("line must convert"); + }; + assert!( + (pl.thickness - 3.5).abs() < 1e-12, + "converted polyline must keep source thickness, got {}", + pl.thickness + ); + } + + #[test] + fn convert_keeps_arc_thickness() { + let mut a = acadrust::entities::Arc::new(); + a.center = acadrust::types::Vector3::new(0.0, 0.0, 0.0); + a.radius = 5.0; + a.start_angle = 0.0; + a.end_angle = std::f64::consts::FRAC_PI_2; + a.thickness = -2.0; + let Some(EntityType::LwPolyline(pl)) = convert_to_polyline(&EntityType::Arc(a)) else { + panic!("arc must convert"); + }; + assert!( + (pl.thickness - (-2.0)).abs() < 1e-12, + "converted polyline must keep negative thickness, got {}", + pl.thickness + ); + } +} + // ── Curve fitting ───────────────────────────────────────────────────────── fn vert_xy(v: &LwVertex) -> DVec2 {