Layer states are ACADMCP_LAYERSTATES XRECORD chunks (<= 255 ASCII chars each), in the file and not in AutoCAD's Layer States Manager. Headless view restore writes the *Active VPORT (ezdxf's header has no $VIEWCTR; measured). UCS is stored and made current; tool coordinates stay WCS. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
55 lines
2.0 KiB
Python
55 lines
2.0 KiB
Python
"""UCS axes, validated and normalised before either engine writes.
|
|
|
|
Tool coordinates stay WCS on both engines (the repository rule): a UCS is
|
|
stored and made current for the operator's benefit, and nothing here starts
|
|
interpreting inputs in it.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import math
|
|
|
|
WORLD = "world"
|
|
ORTHO_TOLERANCE_DEG = 1e-3
|
|
|
|
|
|
def _vec3(value, where: str) -> tuple[float, float, float]:
|
|
if not isinstance(value, (list, tuple)) or len(value) != 3:
|
|
raise ValueError(f"{where} must be an [x, y, z] triple")
|
|
out = []
|
|
for axis, item in zip("xyz", value, strict=True):
|
|
if isinstance(item, bool) or not isinstance(item, (int, float)):
|
|
raise ValueError(f"{where}.{axis} must be a number, got {item!r}")
|
|
number = float(item)
|
|
if not math.isfinite(number):
|
|
raise ValueError(f"{where}.{axis} must be finite")
|
|
out.append(number)
|
|
return out[0], out[1], out[2]
|
|
|
|
|
|
def _unit(vector, where: str) -> tuple[float, float, float]:
|
|
length = math.sqrt(sum(c * c for c in vector))
|
|
if length < 1e-12:
|
|
raise ValueError(f"{where} must not be a zero vector")
|
|
return vector[0] / length, vector[1] / length, vector[2] / length
|
|
|
|
|
|
def resolve_ucs_axes(origin, x_axis, y_axis) -> dict:
|
|
"""Unit axes plus the measured angle; non-orthogonal axes are refused by that angle."""
|
|
o = _vec3(origin, "origin")
|
|
x = _unit(_vec3(x_axis, "x_axis"), "x_axis")
|
|
y = _unit(_vec3(y_axis, "y_axis"), "y_axis")
|
|
dot = max(-1.0, min(1.0, sum(a * b for a, b in zip(x, y, strict=True))))
|
|
angle = math.degrees(math.acos(dot))
|
|
if abs(angle - 90.0) > ORTHO_TOLERANCE_DEG:
|
|
raise ValueError(
|
|
f"ucs_set: x_axis and y_axis must be perpendicular; measured {angle:.2f}° "
|
|
f"between them (tolerance {ORTHO_TOLERANCE_DEG}°)"
|
|
)
|
|
z = (
|
|
x[1] * y[2] - x[2] * y[1],
|
|
x[2] * y[0] - x[0] * y[2],
|
|
x[0] * y[1] - x[1] * y[0],
|
|
)
|
|
return {"origin": o, "x_axis": x, "y_axis": y, "z_axis": z, "angle_deg": angle}
|