fix(task-9): signed DIMTP/DIMTM, read-side arrowhead spelling, and the Task 10 handshake
Two blocking review findings on the standards data layer:
* DIMTP and DIMTM were pinned to ("float", 0.0, 1e6), refusing every signed
deviation with a false "must be at least 0". AutoCAD documents both as
signed and the repo already relies on it: build_dim_override writes
dimtm=-0.022 for ISO 286 p6 and dimtp=-0.020 for f7, and
dimension_linear(fit=...) emits exactly those. A dimension style could
therefore never hold a double-positive or double-negative fit that the same
server draws correctly per dimension, and an agent obeying the message
would ship a sign-flipped tolerance. Both are now ("float", -1e6, 1e6); a
provenance test resolves the p6/f7/H7/h6 overrides build_dim_override
emits through resolve_dimstyle and pins the signs.
* 827719f made check_dim_value return DIMBLK/DIMBLK1/DIMBLK2 in the
canonical _OBLIQUE spelling and added ezdxf_arrowhead without telling
Task 10, whose plan helpers store the value verbatim and read the raw
string back - executed as written the headless drawing raised at render
and at doc.write, a user block passed the data layer and failed at save,
and ezdxf's OBLIQUE-in-session / _OBLIQUE-after-reload split made an
idempotent re-set report a change (spec section 8.3). This commit adds the
read-side twin reported_arrowhead (never raises; a foreign name passes
through), pins it with an in-session-equals-reloaded round trip, and edits
the plan: Task 9 Produces and Task 10 Consumes now name ARROWHEAD_BLOCKS,
canonical_arrowhead, ezdxf_arrowhead and reported_arrowhead; Task 10's
_write_dimstyle_values maps through ezdxf_arrowhead, _dimstyle_value and
_com_dimvar_for_report map through reported_arrowhead, a new
_require_arrowhead_blocks refuses a user block absent from doc.blocks
before dimstyle_create/modify write anything (and rewrites the name to the
block table's own spelling, measured to be what a reload reports), and two
headless tests witness the rendered INSERTs, the save/reopen spelling and
the refusal. The plan's own copy of the range table is corrected to match
the module (arrow kind, signed tolerances). The edited helper block was
executed against ezdxf 1.4.4 before being written into the plan.
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
827719fdd0
commit
2e2cd2773d
@@ -22,6 +22,7 @@ from engineering.standards.dimstyles import (
|
||||
check_dim_value,
|
||||
describe_preset,
|
||||
ezdxf_arrowhead,
|
||||
reported_arrowhead,
|
||||
resolve_dimstyle,
|
||||
validate_overrides,
|
||||
)
|
||||
@@ -206,6 +207,51 @@ def test_validate_overrides_alone_is_what_modify_uses():
|
||||
assert check_dim_value("DIMBLK", "_OBLIQUE") == "_OBLIQUE"
|
||||
|
||||
|
||||
# ── tolerances: DIMTP / DIMTM are signed ────────────────────────────────────
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("fit", "expected"),
|
||||
[
|
||||
("p6", {"DIMTP": 0.035, "DIMTM": -0.022}), # double positive: -DIMTM is +0.022
|
||||
("f7", {"DIMTP": -0.02, "DIMTM": 0.041}), # double negative: DIMTP below zero
|
||||
("H7", {"DIMTP": 0.021, "DIMTM": 0.0}),
|
||||
("h6", {"DIMTP": 0.0, "DIMTM": 0.013}),
|
||||
],
|
||||
)
|
||||
def test_a_style_holds_the_same_signed_deviations_a_dimension_gets(fit, expected):
|
||||
"""``dimension_linear(fit=...)`` writes ``build_dim_override``'s DIMTP/DIMTM
|
||||
onto one dimension; a dimension style must accept the identical numbers,
|
||||
signs included, or a p6 / f7 fit can be drawn but never styled — and an
|
||||
agent obeying an "at least 0" refusal would ship a sign-flipped tolerance."""
|
||||
from engineering.fits import fit_lookup
|
||||
from engineering.tolerances import build_dim_override
|
||||
|
||||
deviation = fit_lookup(fit, 20.0)
|
||||
override, _ = build_dim_override(deviation.upper_mm, -deviation.lower_mm, "deviation", None)
|
||||
styled = {k: v for k, v in override.items() if k.upper() in DIM_VARIABLE_WHITELIST}
|
||||
assert {"dimtol", "dimtp", "dimtm"} <= set(styled)
|
||||
typed = resolve_dimstyle("iso-25", styled)
|
||||
assert typed["DIMTOL"] == 1
|
||||
assert typed["DIMTP"] == pytest.approx(expected["DIMTP"])
|
||||
assert typed["DIMTM"] == pytest.approx(expected["DIMTM"])
|
||||
assert typed["DIMTM"] == pytest.approx(override["dimtm"]), "sign preserved, not abs()"
|
||||
assert typed["DIMTP"] == pytest.approx(override["dimtp"])
|
||||
|
||||
|
||||
@pytest.mark.parametrize("var", ["DIMTP", "DIMTM"])
|
||||
def test_tolerance_limits_are_signed_finite_floats(var):
|
||||
assert DIM_VARIABLE_RANGES[var] == ("float", -1e6, 1e6)
|
||||
assert check_dim_value(var, -0.022) == -0.022
|
||||
assert check_dim_value(var, 0) == 0.0
|
||||
with pytest.raises(ValueError, match=var):
|
||||
check_dim_value(var, float("-inf"))
|
||||
with pytest.raises(ValueError, match=var):
|
||||
check_dim_value(var, -1e7)
|
||||
with pytest.raises(TypeError, match=var):
|
||||
check_dim_value(var, "-0.022")
|
||||
|
||||
|
||||
# ── arrowheads: DIMBLK / DIMBLK1 / DIMBLK2 ──────────────────────────────────
|
||||
|
||||
|
||||
@@ -298,6 +344,47 @@ def test_the_canonical_spelling_is_not_what_the_headless_writer_takes():
|
||||
doc.write(io.StringIO())
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("stored", "reported"),
|
||||
[
|
||||
("OBLIQUE", "_OBLIQUE"), # what ezdxf holds in-session after the writer set it
|
||||
("_OBLIQUE", "_OBLIQUE"), # what ezdxf holds after a reload; what COM reports
|
||||
("", ""),
|
||||
(None, ""),
|
||||
("_CLOSEDFILLED", ""),
|
||||
("MYARROW", "MYARROW"),
|
||||
("MY ARROW,1", "MY ARROW,1"), # a foreign drawing's name: passed through, never raised
|
||||
(" ", " "),
|
||||
],
|
||||
)
|
||||
def test_reported_arrowhead_spells_a_stored_name_one_way_and_never_raises(stored, reported):
|
||||
assert reported_arrowhead(stored) == reported
|
||||
|
||||
|
||||
@pytest.mark.parametrize("canonical", ["_OBLIQUE", "_DOT", "", "MYARROW"])
|
||||
def test_a_style_reports_the_same_arrowhead_before_and_after_a_reload(canonical):
|
||||
"""The spec §8.3 witness at the data layer: a raw read spells ``OBLIQUE``
|
||||
in-session and ``_OBLIQUE`` after reopen, so ``dimstyle_modify`` re-setting
|
||||
the same value would report a change. Through ``reported_arrowhead`` the two
|
||||
reads agree with each other and with what ``check_dim_value`` accepts."""
|
||||
import io
|
||||
|
||||
import ezdxf
|
||||
|
||||
doc = ezdxf.new("R2018")
|
||||
if canonical == "MYARROW":
|
||||
doc.blocks.new("MYARROW").add_line((0, 0), (1, 1))
|
||||
style = doc.dimstyles.new("X")
|
||||
style.dxf.dimblk = ezdxf_arrowhead(canonical)
|
||||
in_session = reported_arrowhead(style.dxf.dimblk)
|
||||
buffer = io.StringIO()
|
||||
doc.write(buffer)
|
||||
buffer.seek(0)
|
||||
reloaded = reported_arrowhead(ezdxf.read(buffer).dimstyles.get("X").dxf.dimblk)
|
||||
assert in_session == reloaded == canonical
|
||||
assert check_dim_value("DIMBLK", in_session) == canonical
|
||||
|
||||
|
||||
# ── the symbol-name rule ────────────────────────────────────────────────────
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user