fix(task-22): ucs_list decides "world" from the frame, not the empty name — an unnamed UCS is its own current row

An unnamed UCS (UCS Origin / 3P without saving, the common kind) has an
empty UCSNAME exactly like WCS does, so both engines answered
`current: world` while the drawing was not in WCS — verified live on
AutoCAD 2026 (`_.UCS _O 10,10,0` → UCSNAME="" WORLDUCS=0) and on the DXF
it writes ($UCSNAME='' $UCSORG=(0,20,0)). A silent wrong value.

- COM: a named match still wins; otherwise WORLDUCS (AutoCAD's own answer)
  decides, and WORLDUCS=0 appends a `name: None` row built from
  UCSORG/UCSXDIR/UCSYDIR.
- ezdxf: the same rule against $UCSORG/$UCSXDIR/$UCSYDIR via the new
  `engineering.environment.ucs.is_world_axes`; a stale $UCSNAME falls back
  to the frame instead of leaving no row current.
- Server `ucs_list` result: `current` is null for an unnamed UCS (never
  `world`), plus `current_unnamed: bool` so null is unambiguous.
- Tests: helper, headless (unnamed row, stale name, and the exact AutoCAD
  DXF over the wire), and COM fake reading WORLDUCS + the frame variables.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
This commit is contained in:
Umut CAN
2026-09-17 15:10:00 +03:00
co-authored by Claude Opus 5
parent 4bf686e943
commit 64cec37cc7
6 changed files with 209 additions and 19 deletions
+27 -8
View File
@@ -2692,22 +2692,41 @@ class EzdxfBackend(AutoCADBackend):
}
async def ucs_list(self) -> list[dict]:
from engineering.environment.ucs import WORLD
from engineering.environment.ucs import (
WORLD,
WORLD_ORIGIN,
WORLD_X_AXIS,
WORLD_Y_AXIS,
is_world_axes,
)
def _sync():
doc = self._require_doc()
current = str(doc.header.get("$UCSNAME", "") or "")
rows = [self._ucs_row(WORLD, (0, 0, 0), (1, 0, 0), (0, 1, 0), current == "")]
header = doc.header
current = str(header.get("$UCSNAME", "") or "")
origin = tuple(header.get("$UCSORG", WORLD_ORIGIN))
x_axis = tuple(header.get("$UCSXDIR", WORLD_X_AXIS))
y_axis = tuple(header.get("$UCSYDIR", WORLD_Y_AXIS))
rows = [self._ucs_row(WORLD, WORLD_ORIGIN, WORLD_X_AXIS, WORLD_Y_AXIS, False)]
named_current = False
for ucs in doc.ucs:
is_current = current != "" and ucs.dxf.name.lower() == current.lower()
named_current = named_current or is_current
rows.append(
self._ucs_row(
ucs.dxf.name,
ucs.dxf.origin,
ucs.dxf.xaxis,
ucs.dxf.yaxis,
current != "" and ucs.dxf.name.lower() == current.lower(),
ucs.dxf.name, ucs.dxf.origin, ucs.dxf.xaxis, ucs.dxf.yaxis, is_current
)
)
if named_current:
return rows
# No saved entry is current. An empty $UCSNAME is NOT "world": an
# unnamed UCS (UCS Origin / 3P without saving) has an empty name
# too, and AutoCAD writes exactly that. The frame decides, the way
# WORLDUCS does live.
if is_world_axes(origin, x_axis, y_axis):
rows[0]["current"] = True
else:
rows.append(self._ucs_row(None, origin, x_axis, y_axis, True))
return rows
return await self._async(_sync)