From c7a7022293644df7f9c5aab96ce7aaa2cf1eae35 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Sun, 26 Apr 2026 22:12:56 +0300 Subject: [PATCH] fix: Polyline3D spline vertex flags and AttributeEntity/Definition DXF rotation Polyline3D tessellation now separates spline-fit curve vertices (flag 8) from frame control points (flag 16): the curve vertices form the rendered wire while control points are exposed as key_vertices for snap. Polylines without spline flags are unchanged. DXF post-load fixup now also converts AttributeEntity and AttributeDefinition rotation from degrees to radians (group code 50 bug), matching the existing LinearDimension fix. Attribute text rendered from DXF files now has correct orientation. Co-Authored-By: Claude Sonnet 4.6 --- src/entities/polyline.rs | 30 +++++++++++++++++++++--------- src/io/mod.rs | 19 ++++++++++++++----- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/entities/polyline.rs b/src/entities/polyline.rs index 523fe70e4..9bcfd38d6 100644 --- a/src/entities/polyline.rs +++ b/src/entities/polyline.rs @@ -298,18 +298,30 @@ impl Transformable for Polyline2D { // ── Polyline3D ──────────────────────────────────────────────────────────────── fn tessellate_polyline3d(pl: &Polyline3D) -> TruckEntity { - let pts: Vec<[f32; 3]> = pl - .vertices - .iter() - .map(|v| [v.position.x as f32, v.position.y as f32, v.position.z as f32]) - .collect(); + let to_pt = |v: &acadrust::entities::Vertex3DPolyline| -> [f32; 3] { + [v.position.x as f32, v.position.y as f32, v.position.z as f32] + }; - let mut points = pts.clone(); - if pl.is_closed() && pts.len() >= 2 { - points.push(pts[0]); + // DXF vertex flags: 8 = spline-fit curve point, 16 = spline frame control point. + // When spline-fit vertices are present use them for the wire and control points for snap; + // otherwise treat all vertices uniformly. + let spline_curve: Vec<_> = pl.vertices.iter().filter(|v| v.flags & 8 != 0).collect(); + let ctrl_pts: Vec<_> = pl.vertices.iter().filter(|v| v.flags & 16 != 0).collect(); + + let (wire_pts, key_verts) = if !spline_curve.is_empty() { + let wire: Vec<[f32; 3]> = spline_curve.iter().map(|v| to_pt(v)).collect(); + let ctrl: Vec<[f32; 3]> = ctrl_pts.iter().map(|v| to_pt(v)).collect(); + (wire, ctrl) + } else { + let pts: Vec<[f32; 3]> = pl.vertices.iter().map(to_pt).collect(); + (pts.clone(), pts) + }; + + let mut points = wire_pts.clone(); + if pl.is_closed() && wire_pts.len() >= 2 { + points.push(wire_pts[0]); } - let key_verts = pts.clone(); TruckEntity { object: TruckObject::Lines(points), snap_pts: vec![], diff --git a/src/io/mod.rs b/src/io/mod.rs index 26cd5ed64..6bef9e876 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -170,13 +170,22 @@ pub fn save_dxf(doc: &CadDocument, path: &Path) -> Result<(), String> { // ── DXF post-load fixups ────────────────────────────────────────────────── -/// The acadrust DXF reader stores LinearDimension.rotation directly from DXF -/// group code 50, which is in degrees. DWG and our own dimension-creation code -/// store it in radians. Convert so tessellation can call cos/sin uniformly. +/// The acadrust DXF reader stores several rotation fields directly from DXF +/// group code 50 in degrees, while DWG and our own creation code store radians. +/// Apply to_radians() on load so tessellation can call cos/sin uniformly. fn fix_dxf_dimension_rotations(doc: &mut CadDocument) { for entity in doc.entities_mut() { - if let EntityType::Dimension(Dimension::Linear(d)) = entity { - d.rotation = d.rotation.to_radians(); + match entity { + EntityType::Dimension(Dimension::Linear(d)) => { + d.rotation = d.rotation.to_radians(); + } + EntityType::AttributeDefinition(a) => { + a.rotation = a.rotation.to_radians(); + } + EntityType::AttributeEntity(a) => { + a.rotation = a.rotation.to_radians(); + } + _ => {} } } }