diff --git a/src/entities/lwpolyline.rs b/src/entities/lwpolyline.rs index 06802fc0f..a189d9739 100644 --- a/src/entities/lwpolyline.rs +++ b/src/entities/lwpolyline.rs @@ -504,6 +504,18 @@ fn apply_geom_prop(pline: &mut LwPolyline, field: &str, value: &str) { } else { value == "true" }; + // Closing a polyline whose last vertex already sits on the first + // would stack two control points there (a polyline drawn back to + // its start point and then closed) — drop the duplicate so the + // closing segment replaces it (#421). + if pline.is_closed && pline.vertices.len() > 2 { + let (first, last) = (&pline.vertices[0], pline.vertices.last().unwrap()); + let d2 = (first.location.x - last.location.x).powi(2) + + (first.location.y - last.location.y).powi(2); + if d2 < 1e-12 { + pline.vertices.pop(); + } + } return; } "plinegen" => { diff --git a/src/modules/draw/draw/polyline.rs b/src/modules/draw/draw/polyline.rs index 8157871fb..02beb6f0d 100644 --- a/src/modules/draw/draw/polyline.rs +++ b/src/modules/draw/draw/polyline.rs @@ -270,6 +270,20 @@ impl CadCommand for PlineCommand { self.bulges[last_idx] = bulge; } + // A point landing back on the FIRST vertex (endpoint snap) closes the + // polyline instead of stacking a duplicate vertex there — the segment + // bulge just computed above already describes the closing segment + // (#421). Line mode needs two real segments first so a doubled-back + // line isn't "closed"; an arc segment closes from two vertices. + if let Some(first) = self.vertices.first() { + let enough = self.vertices.len() >= 3 + || (self.vertices.len() == 2 && matches!(self.mode, SegMode::Arc)); + let d2 = (pt.x - first.x).powi(2) + (pt.y - first.y).powi(2); + if enough && d2 < 1e-12 { + return self.sync_live(true, true); + } + } + self.vertices.push(pt); self.bulges.push(0.0); // Publish to the document as soon as the polyline has a segment so it