diff --git a/src/draftwright/annotations/_gdt.py b/src/draftwright/annotations/_gdt.py index fab1ac43..5c98740b 100644 --- a/src/draftwright/annotations/_gdt.py +++ b/src/draftwright/annotations/_gdt.py @@ -787,7 +787,7 @@ def render_gdt( # Prefer the one nearest this strip's anchor; the farther datum retains # the ordinary side/sheet fallback if its first corridor becomes full. datum_stem_rank = 0 - if item.kind == "datum_ref": + if item.kind == "datum_ref" and strip is not None: perp, stack = (px, py) if horizontal else (py, px) distance = abs(stack - strip.anchor) for other in items: diff --git a/tests/test_adr0018_view_selection.py b/tests/test_adr0018_view_selection.py index 23a61b13..ddd740a9 100644 --- a/tests/test_adr0018_view_selection.py +++ b/tests/test_adr0018_view_selection.py @@ -597,26 +597,22 @@ def _plate(): return part @pytest.mark.slow - def test_the_smaller_view_set_reaches_a2_and_what_it_costs(self): + def test_automatic_view_set_matches_manual_reduction_on_a2(self): part = self._plate() full = build_drawing(part) reduced = build_drawing(part, _views=("front", "side")) - # The ADR's failure: the fixed four-view topology drives A1 at 1:1. - assert (full.page_w, full.page_h) == (841.0, 594.0) + # Automatic view selection now removes the redundant plan view. + assert (full.page_w, full.page_h) == (594.0, 420.0) assert full.scale == 1.0 + assert set(full.views) == {"front", "side", "iso"} - # Dropping the redundant plan reaches the ADR's target sheet at the same scale. - assert (reduced.page_w, reduced.page_h) == (594.0, 420.0) + # The explicit two-principal view set reaches the same finished drawing. + assert (reduced.page_w, reduced.page_h) == (full.page_w, full.page_h) assert reduced.scale == full.scale - - # The omitted plan centerline and ISO caption are furniture, not requirements. - # Independent lint confirms that the smaller view set keeps supported meaning. - assert len(reduced.annotations()) < len(full.annotations()) - assert set(full.annotations()) - set(reduced.annotations()) == { - "centerline_plan", - "note_iso_nts", - } + assert set(reduced.views) == set(full.views) + assert set(reduced.annotations()) == set(full.annotations()) + assert _lint(full) == set() assert _lint(reduced) == set() @pytest.mark.slow diff --git a/tests/test_gdt_placement.py b/tests/test_gdt_placement.py index 5a4be2d9..46a91b57 100644 --- a/tests/test_gdt_placement.py +++ b/tests/test_gdt_placement.py @@ -325,6 +325,42 @@ def forbidden_sheet_route(*_args, **_kwargs): assert [attempt["side"] for attempt in events[0]["items"][0]["attempts"]] == ["left"] +def test_imported_datum_on_absent_side_strip_drops_without_aborting_issue_2182(tmp_path): + datum = DatumRef( + frame=Frame((0.0, -25.0, 0.0), "y"), + letter="A", + view="side", + side="left", + source_id="datum:missing-side-strip", + reference_surface_kind="plane", + ) + surviving_frame = ControlFrame( + frame=Frame((0.0, 0.0, 10.0), "z"), + characteristic="flatness", + tolerance="0.05", + view="plan", + side="above", + source_id="geometric_tolerance:survivor", + ) + trace_path = tmp_path / "missing-side-strip.json" + + dwg = _build(datum, surviving_frame, pmi="annotate", trace=trace_path) + + assert "m_gdt0" not in dwg.annotations() + assert "m_gdt1" in dwg.annotations() + assert any( + issue.code == "pmi_dropped" and "m_gdt0" in issue.message for issue in dwg.registry.issues + ) + solves = json.loads(trace_path.read_text())["solves"] + assert any( + solve["corridor"] == ["side", "left"] + and solve["strip"] is None + and {item["name"] for item in solve["candidates"]} == {"m_gdt0"} + and solve["outcomes"] == [{"name": "m_gdt0", "outcome": "dropped", "reason": "no_strip"}] + for solve in solves + ) + + def test_projected_datum_stem_keeps_both_near_and_far_datums_issue_2128(): part = Box(80, 50, 20) near = DatumRef(frame=Frame((0, 0, -10), "z"), letter="A", view="front", side="below") diff --git a/tests/test_issue_1298_manufacturing_requirements.py b/tests/test_issue_1298_manufacturing_requirements.py index 78d73c66..973a865e 100644 --- a/tests/test_issue_1298_manufacturing_requirements.py +++ b/tests/test_issue_1298_manufacturing_requirements.py @@ -1781,13 +1781,9 @@ def test_exact_grm03_renders_complete_source_owned_manufacturing_drawing_once(): assert hashlib.sha256(GRM03.read_bytes()).hexdigest() == GRM03_SHA256 drawing = build_drawing(GRM03, pmi="annotate") - assert (drawing.page_w, drawing.page_h, drawing.scale) == (297.0, 210.0, 2.0) - assert {"front", "side", "detail_a"} <= set(drawing.views) - assert drawing.get_annotation("detail_caption_A").label == ( - "DETAIL A — PARTIAL PROFILE — SCALE 10:1" - ) - assert drawing.detail_decisions[0]["status"] == "placed" - assert drawing.detail_decisions[0]["fit"]["within_reservation"] is True + assert (drawing.page_w, drawing.page_h, drawing.scale) == (297.0, 210.0, 5.0) + assert {"front", "side"} <= set(drawing.views) + assert drawing.detail_decisions == [] assert drawing.scale_decision["status"] != "invalid" expected_manufacturing = { @@ -1951,8 +1947,9 @@ def test_exact_grm03_renders_complete_source_owned_manufacturing_drawing_once(): if isinstance(feature, ChamferFeature) ] == chamfers replayed = namespace["sheet"].build() + # The declared replay still composes the optional detail at its own chosen scale. + assert (replayed.page_w, replayed.page_h, replayed.scale) == (297.0, 210.0, 2.0) assert replayed.detail_decisions[0]["status"] == "placed" - assert replayed.detail_decisions[0]["extent"] == drawing.detail_decisions[0]["extent"] assert replayed.detail_decisions[0]["fit"]["within_reservation"] is True replayed_model = replayed.model() assert sum(isinstance(feature, GeneralTolerance) for feature in replayed_model.features) == 1