Skip to content

Include a polyline's width in its bounding box - #1248

Open
redbluevn wants to merge 2 commits into
DomCR:masterfrom
redbluevn:moredwg/pr82-polyline-width-box
Open

Include a polyline's width in its bounding box#1248
redbluevn wants to merge 2 commits into
DomCR:masterfrom
redbluevn:moredwg/pr82-polyline-width-box

Conversation

@redbluevn

Copy link
Copy Markdown

Stacked on #1224. The first commit on this branch belongs to that pull request - a mirrored polyline reporting its bounding box in object coordinates - because this change touches the same two methods and would conflict with it otherwise. Review only the second commit, or take this one after #1224 lands.

The problem

A polyline can carry a width, and it is drawn half on each side of the centre line. LwPolyline.GetBoundingBox and Polyline<T>.GetBoundingBox measure the centre line only:

IEnumerable<XYZ> points = this.Vertices.Any(v => v.Bulge != 0)
    ? this.GetPoints<XYZ>(byte.MaxValue)
    : this.Vertices.Select(v => v.Location.Convert<XYZ>());

return BoundingBox.FromPoints(points.Select(p => toWorld * p));

So an entity that covers a strip is reported as the line down its middle, and a drawing whose outermost geometry is a wide polyline measures smaller than it is.

How it was found

Asking AutoCAD for the extents of a 17.6 MB client drawing and comparing:

AutoCAD   (457057.490, -121881.651) .. (1147751.416, 251826.623)
ACadSharp (457132.493, -121806.650) .. (1147676.421, 251751.625)

Exactly 75 units short on all four sides — and the entity at the edge is one LWPOLYLINE with ConstantWidth = 150.

The fix

Bound each segment on its own, so a taper is paid for only where it occurs:

  • a straight segment sweeps the strip between its two offset edges, and the offset goes perpendicular to it. Padding in every direction instead would push the box past the flat end cap, and AutoCAD stops at the cap;
  • a segment with a bulge is padded by the larger of its two half widths, which contains the arc's sweep;
  • a bulge over a segment of no length has no arc (Arc.CreateFromBulge refuses the zero radius), so it contributes the point it sits on, widened.

The widened area is measured in the polyline's own plane and taken to the world afterwards, so a mirrored polyline still lands on the correct side. ConstantWidth (code 43) applies wherever a vertex carries none of its own; for the old-style POLYLINE the polyline's own start and end width (40 and 41) play that role. A 3D polyline carries the fields but no width and is untouched.

Measured against AutoCAD, on drawings it minted itself

fixture AutoCAD before after
closed square, ConstantWidth 150 (-75,-75)..(1075,1075) (0,0)..(1000,1000) exact
open PLINE, width 150 (0,-75)..(1075,1000) (0,0)..(1000,1000) exact
open, widths tapering 0-200-400 (0,-108.9)..(1200,1000) (0,0)..(1000,1000) (0,-100)..(1200,1000)

What is left in the third row is the fill AutoCAD adds where two segments meet, which reaches a little further than either segment does.

On the drawing that prompted this, the extents now match AutoCAD to 3e-06. On nineteen other client drawings not one extent moves, and a polyline with no width keeps exactly the path it had.

Tests

GetBoundingBoxIncludesAConstantWidth, GetBoundingBoxCarriesAWidthAcrossTheSegmentAndNotBeyondItsEnds, GetBoundingBoxKeepsTheCentreLineWithoutAWidth, and GetBoundingBoxIncludesTheWidthTheVertexesCarry for the old-style polyline. Suite 2900 / 0.

MoreDwg and others added 2 commits September 5, 2026 14:34
…t coordinates

The vertexes of an LWPOLYLINE or a 2D POLYLINE, and the centre of a CIRCLE, are
stored in the entity's own object coordinate system. GetBoundingBox returned
them unchanged, so for the (0,0,-1) extrusion AutoCAD writes whenever geometry
is mirrored the box came back with the sign of X untouched - the entity placed
on the opposite side of the drawing from where it belongs.

Insert and Arc already go through Matrix4.GetArbitraryAxis for exactly this
reason; these three did not.

One mirrored polyline is enough to do real damage, because it is usually inside
a block: a single one in a block definition of an architectural drawing measured
here put its own block's bounding box 8.5 million units wide instead of 208, and
with it every one of the 44 references to that block.

Polyline3D is left alone - its vertexes are already world coordinates - through
a flag on the shared base rather than by relying on its normal staying +Z.

LwPolyline's GetBoundingBoxTest was an empty method body; it now covers both the
plain and the mirrored case, and CircleTests gained the mirrored case.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A polyline with width covers half of it on each side of the centre line, and
AutoCAD's extents include that. The bounding box measured the centre line alone,
so a drawing whose outermost entity is a wide polyline reported smaller than it
is: on a 17.6 MB client drawing the extents came out exactly 75 units short on
all four sides, the half width of one LWPOLYLINE with ConstantWidth 150.

Each segment is bounded on its own so a taper is only paid for where it occurs.
A straight segment sweeps the strip between its two offset edges, and the offset
goes perpendicular to it - padding in every direction instead would push the box
past the flat end cap, and AutoCAD stops at the cap. A segment with a bulge is
padded by the larger of its two half widths, which contains its sweep. The
widened area is measured in the polyline's own plane and taken to the world
afterwards, so a mirrored polyline still lands on the correct side.

Checked against AutoCAD on drawings it minted itself:

  closed square, ConstantWidth 150     AutoCAD (-75,-75)..(1075,1075)   exact
  open PLINE, width 150                AutoCAD (0,-75)..(1075,1000)     exact
  open, widths tapering 0-200-400      AutoCAD (0,-108.9)..(1200,1000)
                                       measured (0,-100)..(1200,1000)

The remainder in the third case is the fill AutoCAD adds where two segments
meet, which reaches slightly further than either segment does.

A polyline without any width keeps the centre-line path exactly as before, and
the 3D polyline - whose vertexes carry the fields but no width - is untouched.
On the nineteen client drawings not one extent moves; on the drawing that
prompted this, the extents now match AutoCAD to 3e-06 where they were 75 units
short. Suite 2900/0 with four new cases.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VMhDpZ1rVxTVqbDPqUoYhk
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant