fix(task-15): angle sysvars are radians on both engines; OSMODE is registry-saved and refused headlessly

Three authored claims in SYSVAR_CATALOG were false against the engines:

- HPANG said "degrees" but ActiveX GetVariable/SetVariable (and AutoLISP
  getvar) hold radians - measured on AutoCAD 2026 in a scratch document:
  SetVariable(pi/4) reads back 0.7853981633974483 on both paths. The row
  now says radians, like POLARANG.
- ANGBASE said "radians" with both engines True, but the DXF header stores
  $ANGBASE in degrees (AutoCAD saved the 1.5708-radian seat value as 90.0)
  and ezdxf read/wrote it raw, so the two engines disagreed by a factor of
  57.3. EzdxfBackend now translates that header variable at its boundary
  (_HEADER_DEGREE_SYSVARS): the tool speaks radians on both engines, the
  file keeps degrees.
- OSMODE claimed engines.ezdxf True, but $OSMODE is an R12-only header
  variable ezdxf never exports for R2000+: a headless write reported
  ok: True and read back None after save + reopen. It is registry-saved
  in AutoCAD, so it joins _UNSAVED_SYSVARS (refused with
  capability: registry_sysvar; the facade's `osmode` key refuses and
  snapshots None headlessly, like polar / polar_angle). Headless reads of
  every registry-saved variable now report None instead of whatever the
  ezdxf template holds in memory.

The gate that should have caught all three, tests/test_sysvar_catalog.py's
default round-trip, wrote 56 no-ops out of 66 and never saved: it now
writes a value that differs from the fresh document's for every writable
ezdxf-True entry, saves, reopens and reads back from the file. Two new
tests pin OSMODE's refusal and ANGBASE's radians-at-the-tool /
degrees-in-the-file split.

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:20:02 +03:00
co-authored by Claude Opus 5
parent 125a8ed3b8
commit 75c13196b9
5 changed files with 175 additions and 37 deletions
+5 -2
View File
@@ -44,8 +44,9 @@ class SettingsContract:
unknown key; a value outside ``_SETTING_RANGES``; a malformed
``limits`` / ``annotation_scale``; ``annotation_scale`` on an R12 file
headlessly (no OBJECTS section, so the value would vanish at save —
save as R2000 or newer first); ``polar`` / ``polar_angle`` on the
headless engine (registry-saved — ``capability: registry_sysvar``);
save as R2000 or newer first); ``osmode`` / ``polar`` / ``polar_angle``
on the headless engine (registry-saved — ``capability: registry_sysvar``;
a snapshot reports them as ``None`` there, a file holds no value);
``dimstyle`` / ``textstyle`` when the backend has no styles contract.
Note that a no-argument snapshot is one ``system_get_variable`` per key
@@ -145,6 +146,8 @@ _SETTING_MAP: dict[str, tuple[str, str]] = {
"text_size": ("TEXTSIZE", "float"),
"point_mode": ("PDMODE", "int"),
"point_size": ("PDSIZE", "float"),
# OSMODE is registry-saved (a DXF R2000+ file has no $OSMODE), so the
# headless engine refuses it like the polar pair below.
"osmode": ("OSMODE", "int"),
"fillet_radius": ("FILLETRAD", "float"),
# ── track E: the environment a drawing lives in ──────────────────────