fix(task-14): polyline bulges leave the backends in WCS, so a mirrored arc reads on its true side
The per-vertex `bulges` list was handed out in the entity's own frame next
to `points` that had been translated to WCS. A mirrored LWPOLYLINE
(extrusion / Normal -Z, what `entity_mirror` produces) is a reflection,
which reverses every arc's sense, so the reader tested endpoints against
the arc's mirror image: a line ending on the true apex dangled, a line
ending on empty paper made a junction, and `length` (frame-invariant)
said nothing. Both engines had it — ezdxf emitted `get_points("xyb")`
raw, COM returned `GetBulge` raw after reflecting `Coordinates`.
`ocs.wcs_bulge(extrusion, bulge)` now negates the sign for a left-handed
frame (the same rule `wcs_arc_angles` applies to an ARC's endpoints) and
both `_entity_info` paths route bulges through it whenever they translate
the points. Fenced by an end-to-end mirrored-pipe graph test, the COM
`_entity_info` fake with Normal -Z, and a boundary-level OCS test.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
This commit is contained in:
co-authored by
Claude Opus 5
parent
8fcd587b5b
commit
8b6f355c3d
@@ -98,3 +98,29 @@ def plane_normal(extrusion) -> list[float]:
|
||||
"""The entity's plane normal, for reporting a frame that xy cannot express."""
|
||||
uz = OCS(Vec3(extrusion)).uz
|
||||
return [float(uz.x), float(uz.y), float(uz.z)]
|
||||
|
||||
|
||||
def wcs_bulge(extrusion, bulge) -> float:
|
||||
"""An LWPOLYLINE vertex bulge as the arc reads in WCS XY.
|
||||
|
||||
The bulge is ``tan(sweep/4)``, signed counter-clockwise *in the entity's
|
||||
own frame*. A left-handed frame (``uz.z < 0`` — what a mirror produces) is
|
||||
a reflection of WCS XY, and a reflection reverses every arc's sense: the
|
||||
same stored ``+1`` that bowed to -y before ``entity_mirror`` bows to +y
|
||||
after it, while ``points`` translated through :func:`to_wcs_2d` land where
|
||||
the vertices really are. Handing the stored sign out next to translated
|
||||
points describes the arc's mirror image, and nothing in the payload can
|
||||
show it (``length`` is frame-invariant). Same rule as
|
||||
:func:`wcs_arc_angles` reversing an ARC's endpoints.
|
||||
|
||||
The magnitude is left alone: it is exact for every flat frame, and in a
|
||||
tilted plane the projected arc is no longer circular at all, which
|
||||
``plane_normal`` already reports.
|
||||
"""
|
||||
value = float(bulge or 0.0)
|
||||
if value == 0.0:
|
||||
return 0.0
|
||||
try:
|
||||
return -value if OCS(Vec3(extrusion)).uz.z < 0 else value
|
||||
except Exception:
|
||||
return value # an unreadable extrusion is not evidence of a reflected frame
|
||||
|
||||
Reference in New Issue
Block a user