diff --git a/crates/cad/src/scene.rs b/crates/cad/src/scene.rs index abd2d48d..a8a9aa1d 100644 --- a/crates/cad/src/scene.rs +++ b/crates/cad/src/scene.rs @@ -652,8 +652,10 @@ impl<'a> Scene<'a> { let h = self.handle(); self.e.entity("SPLINE", h, fixed::MODEL_RECORD, &layer, style, "AcDbSpline"); self.e.normal(); - // Planar. - self.e.int(70, 8); + // Planar (bit 3) and, if the subpath is closed, also closed (bit 0). A geometrically + // closed cut needs the closed flag set so CAM/CAM tools (e.g. VCarve) treat it as a + // pocket/profile rather than asking the operator to join endpoints. (#1299) + self.e.int(70, if sub.closed { 8 | 1 } else { 8 }); self.e.int(71, 3); self.e.int(72, knots.len() as i64); self.e.int(73, ctrl.len() as i64); diff --git a/crates/cad/src/tests.rs b/crates/cad/src/tests.rs index 5d6c50e7..87023aa3 100644 --- a/crates/cad/src/tests.rs +++ b/crates/cad/src/tests.rs @@ -155,6 +155,7 @@ fn lines_are_lwpolylines_curves_splines_and_fills_hatches() { // The ellipse: four cubics through clamped knots. let spline = &e[3]; assert_eq!((get(spline, 71), get(spline, 73)), (Some("3"), Some("13"))); + assert_eq!(get(spline, 70), Some("9"), "closed planar spline (#1299): 1 = closed, 8 = planar"); assert_eq!(all(spline, 40), [0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 4.0, 4.0, 4.0, 4.0]); // R14 has lightweight polylines but no lineweights: the stroke is a polyline width. let r14 = export(&d, &DxfOptions { version: DxfVersion::R14, ..opts(&d) }).unwrap();