From 07d7888b94d0cc4f6d67edb00b627ea1857c7a89 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Fri, 17 Jul 2026 15:00:06 +0300 Subject: [PATCH] =?UTF-8?q?fix(render):=20halve=20polyline=20width=20?= =?UTF-8?q?=E2=80=94=20wide=20bands=20drew=20twice=20as=20wide?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DXF codes 43 / 40 / 41 store a polyline band's full width, and `polyline_segment_fill` offsets ±hw about the centreline, so the stored value has to be halved. Both `wide_fills` passed it in whole and drew every wide polyline at double width. The codebase was writing the spec value and reading it as something else: `make_donut` stores `outer_r - inner_r`, a full width, which `wide_fills` then treated as a half-width. So OCS mis-drew even its own donuts. Seen on budweiser2018.dwg handle 2579, a donut of width 2.2 on a 1.1 centreline radius: it should fill out to radius 2.2 and instead reached 3.3 — a disc 1.5× its real size, overflowing the square it sits in. Affects every wide polyline, so TRACE and PEDIT-widened runs come back to true size too. The writers already stored full width and are left alone, as is the property panel, which shows the file's own value. Co-Authored-By: Claude Opus 4.8 --- src/entities/lwpolyline.rs | 13 ++++++++----- src/entities/polyline.rs | 9 +++++---- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/entities/lwpolyline.rs b/src/entities/lwpolyline.rs index 1a30d12b6..c1a5a3b26 100644 --- a/src/entities/lwpolyline.rs +++ b/src/entities/lwpolyline.rs @@ -662,9 +662,12 @@ impl crate::entities::traits::Transformable for LwPolyline { /// UTM-scale coordinates — building them in absolute f32 collapsed the band into /// a string of squares far from the origin. pub(crate) fn wide_fills(pl: &acadrust::entities::LwPolyline) -> ([f64; 2], Vec>) { - // The stored width is applied to EACH side of the centreline (not halved), - // so `polyline_segment_fill`'s ±offset spans the full width on either side. - let hw_const = pl.constant_width as f32; + // Codes 43 / 40 / 41 store the band's FULL width, and + // `polyline_segment_fill` offsets ±hw about the centreline — so halve it. + // Feeding the stored width in whole draws every wide polyline twice as wide + // as the file asks for, which on a donut (a closed 2-vertex bulge-1 polyline) + // shows up as a disc 1.5× its real radius. + let hw_const = pl.constant_width as f32 * 0.5; let verts = &pl.vertices; let n = verts.len(); if n < 2 { @@ -677,12 +680,12 @@ pub(crate) fn wide_fills(pl: &acadrust::entities::LwPolyline) -> ([f64; 2], Vec< let v0 = &verts[i]; let v1 = &verts[(i + 1) % n]; let hw0 = if v0.start_width > 1e-9 { - v0.start_width as f32 + v0.start_width as f32 * 0.5 } else { hw_const }; let hw1 = if v0.end_width > 1e-9 { - v0.end_width as f32 + v0.end_width as f32 * 0.5 } else { hw_const }; diff --git a/src/entities/polyline.rs b/src/entities/polyline.rs index ff8ae5fae..48ab13c1c 100644 --- a/src/entities/polyline.rs +++ b/src/entities/polyline.rs @@ -770,8 +770,9 @@ impl Transformable for Polyline3D { /// relative to (the first vertex). See `lwpolyline::wide_fills` — offsets are /// f32 from `origin` so the band stays precise at UTM-scale coordinates. pub(crate) fn wide_fills(pl: &acadrust::entities::Polyline2D) -> ([f64; 2], Vec>) { - // Width is applied full to each side of the centreline (not halved). - let hw_default = pl.start_width.max(pl.end_width) as f32; + // The stored widths are the band's FULL width and `polyline_segment_fill` + // offsets ±hw about the centreline — halve them. See `lwpolyline::wide_fills`. + let hw_default = pl.start_width.max(pl.end_width) as f32 * 0.5; let verts = &pl.vertices; let n = verts.len(); if n < 2 { @@ -784,12 +785,12 @@ pub(crate) fn wide_fills(pl: &acadrust::entities::Polyline2D) -> ([f64; 2], Vec< let v0 = &verts[i]; let v1 = &verts[(i + 1) % n]; let hw0 = if v0.start_width > 1e-9 { - v0.start_width as f32 + v0.start_width as f32 * 0.5 } else { hw_default }; let hw1 = if v0.end_width > 1e-9 { - v0.end_width as f32 + v0.end_width as f32 * 0.5 } else { hw_default };