draftwright 0.2.4__tar.gz → 0.2.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {draftwright-0.2.4 → draftwright-0.2.6}/CHANGELOG.md +63 -1
- {draftwright-0.2.4 → draftwright-0.2.6}/PKG-INFO +1 -1
- {draftwright-0.2.4 → draftwright-0.2.6}/pyproject.toml +1 -1
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/__init__.py +3 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/_core.py +41 -5
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/analysis.py +21 -16
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/annotate.py +1 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/annotations/_common.py +12 -4
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/annotations/from_model.py +234 -80
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/annotations/holes.py +506 -30
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/annotations/orchestrator.py +94 -47
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/annotations/sections.py +58 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/builder.py +236 -14
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/cli.py +2 -5
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/drawing.py +582 -9
- draftwright-0.2.6/src/draftwright/fits.py +154 -0
- draftwright-0.2.6/src/draftwright/intents.py +39 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/model/__init__.py +7 -0
- draftwright-0.2.6/src/draftwright/model/declare.py +476 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/model/detect.py +16 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/model/ir.py +13 -1
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/model/planner.py +23 -10
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/registry.py +36 -2
- draftwright-0.2.6/src/draftwright/sheet_dsl.py +244 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/layout_snapshots/box.json +47 -47
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/layout_snapshots/bracket.json +86 -86
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/layout_snapshots/drive_screw_x.json +36 -36
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/layout_snapshots/dshape.json +54 -54
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/layout_snapshots/flange.json +66 -66
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/layout_snapshots/holed_slot.json +71 -71
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/layout_snapshots/plate_holes.json +61 -61
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/layout_snapshots/side_drilled.json +50 -50
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/layout_snapshots/slotted.json +72 -72
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/layout_snapshots/turned_shaft.json +79 -79
- draftwright-0.2.6/tests/test_declare.py +566 -0
- draftwright-0.2.6/tests/test_e2e_standards.py +343 -0
- draftwright-0.2.6/tests/test_fits.py +225 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/test_make_drawing.py +1398 -16
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/test_render_seam.py +7 -2
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/test_strip_layout.py +7 -8
- draftwright-0.2.6/tests/test_tolerances.py +218 -0
- draftwright-0.2.4/tests/test_e2e_standards.py +0 -175
- {draftwright-0.2.4 → draftwright-0.2.6}/.gitignore +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/LICENSE +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/README.md +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/skills/SKILL.md +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/annotations/__init__.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/export.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/features.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/fonts/IBMPlexMono-Regular.ttf +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/fonts/IBMPlexSansCondensed-Regular.ttf +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/fonts/LICENSE-IBMPlexMono-OFL.txt +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/fonts/LICENSE-IBMPlexSansCondensed-OFL.txt +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/fonts/__init__.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/layout.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/linting/__init__.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/linting/coverage.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/linting/issues.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/linting/structural.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/linting/suggest.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/make_drawing.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/pmi.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/projection.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/recognition/__init__.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/recognition/_features.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/recognition/levels.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/recognition/slots.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/recognition/turned.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/repair.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/src/draftwright/sheet.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/fixtures/nist_ctc_01_asme1_ap203.stp +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/fixtures/nist_ctc_01_asme1_ap242.stp +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/fixtures/nist_ctc_02_asme1_ap203.stp +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/fixtures/nist_ctc_02_asme1_ap242.stp +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/fixtures/nist_ctc_03_asme1_ap203.stp +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/fixtures/nist_ctc_03_asme1_ap242.stp +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/fixtures/nist_ctc_04_asme1_ap203.stp +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/fixtures/nist_ctc_04_asme1_ap242.stp +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/fixtures/nist_ctc_05_asme1_ap203.stp +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/fixtures/nist_ctc_05_asme1_ap242.stp +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/test_e2e_slice.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/test_layout.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/test_layout_cleanliness.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/test_layout_property.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/test_layout_snapshot.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/test_lint_structural.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/test_linting.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/test_part_model.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/test_pmi.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/test_recognition.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/test_registry.py +0 -0
- {draftwright-0.2.4 → draftwright-0.2.6}/tests/test_turned_steps.py +0 -0
|
@@ -1,6 +1,68 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## v0.2.6 — 2026-07-05
|
|
4
|
+
|
|
5
|
+
**ADR 0011 — the IR as a public input: declare features, don't only detect them.**
|
|
6
|
+
Detection stays the default, but the caller can now *supply* the feature model, so a
|
|
7
|
+
part you built parametrically reads as its own drawing and misdetection becomes
|
|
8
|
+
recoverable by construction. Plus the first Phase-2 aspect layer (tolerances + fits).
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`build_drawing(part, model=…)`** accepts a caller-supplied `PartModel` (or a
|
|
13
|
+
`Sequence[Feature]`); when given, **detection is skipped** and the auto-pass dimensions
|
|
14
|
+
the declared features. Detection and declaration are two producers of the same IR (#447,
|
|
15
|
+
ADR 0011 Phase 0).
|
|
16
|
+
- **Object → feature constructors** `draftwright.model.hole` / `boss` / `step` / `slot` /
|
|
17
|
+
`pattern` / `envelope` — read a feature's geometry off the build123d object you built (⌀
|
|
18
|
+
from the cylindrical face, axis/location from the bbox), or supply explicit values (#447).
|
|
19
|
+
- **The fluent `Sheet` façade** (`draftwright.Sheet`) — reference the objects you built,
|
|
20
|
+
declare their drawing aspects, `.build()` / `.export()`. `Sheet.from_part()` seeds the
|
|
21
|
+
hybrid override mode from detection (#447, Phase 1).
|
|
22
|
+
- **Toleranced dimensions** — a `±` / limit tolerance on a diameter, step, or hole bore,
|
|
23
|
+
rendered on both the linear and ⌀-callout paths (`Sheet.tolerance(...)`, or a
|
|
24
|
+
`decorations=` side-layer) (#28, Phase 2 P2a).
|
|
25
|
+
- **Fit-class deviation** — `Sheet.fit("H7")` resolves an ISO 286 fit class to its limit
|
|
26
|
+
deviation for the feature's nominal ⌀, rendered as the class code (`ø20 H7`, default) or
|
|
27
|
+
the signed deviations (`show="deviation"` → `ø20 +0.021/0`). Common classes; fails loud
|
|
28
|
+
outside its table (#29, Phase 2 P2a.2).
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- **Object constructors honour explicit overrides** — a passed object supplies *defaults*;
|
|
33
|
+
each explicit keyword overrides that field independently, and invalid public input fails
|
|
34
|
+
at declaration with a clear `ValueError` (#451, #452).
|
|
35
|
+
- **A declared hole/pattern renders at its declared position** even where detection missed
|
|
36
|
+
it — the callout membership is sourced from the declared model, not only detection (#448).
|
|
37
|
+
- **`Sheet.model()` / `Sheet.from_part()`** no longer build a full drawing just to return
|
|
38
|
+
the IR — feature inspection/seeding is now a cheap, no-render path (#453).
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- A narrow diameter band hidden under a larger OD silhouette is no longer silently
|
|
43
|
+
undimensioned (the two feature inventories agreed) (#298).
|
|
44
|
+
|
|
45
|
+
## v0.2.5 — 2026-07-04
|
|
46
|
+
|
|
47
|
+
**The editable write API and record-then-finalize.** A detected drawing became an editable
|
|
48
|
+
object, and a generated `--script` became a runnable reconstruction that reaches auto-pass
|
|
49
|
+
quality.
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
|
|
53
|
+
- **`dwg.model()`** exposes the detected `PartModel` as a read surface, plus feature-
|
|
54
|
+
referenced add verbs `dimension()` / `callout()` / `locate()` / `furniture()` /
|
|
55
|
+
`section()` and `drop(feature)`, with a machine-checked completeness audit (#400).
|
|
56
|
+
- **Record-then-finalize** (#426): the verbs record intents in deferred mode and
|
|
57
|
+
`dwg.finalize()` (auto-run by export) drains them through the auto-pass's own solvers, so
|
|
58
|
+
a reconstruction reaches auto-pass quality; `--script` now emits a runnable detect-only
|
|
59
|
+
reconstruction.
|
|
60
|
+
|
|
61
|
+
### Changed
|
|
62
|
+
|
|
63
|
+
- **Dimension-line spacing now follows ISO 129-1 / ASME Y14.5 convention** (#347): a wider
|
|
64
|
+
first-line gap (8 → 10 mm) and tighter, uniform parallel stacking (between-line clear gap
|
|
65
|
+
4 → 2.5 mm), with the inter-view corridor widened in step. Re-drifts every drawing.
|
|
4
66
|
|
|
5
67
|
## v0.2.4 — 2026-07-03
|
|
6
68
|
|
|
@@ -30,6 +30,7 @@ _LAZY = {
|
|
|
30
30
|
"make_drawing": "draftwright.builder",
|
|
31
31
|
"Drawing": "draftwright.drawing",
|
|
32
32
|
"FeatureInfo": "draftwright.drawing",
|
|
33
|
+
"Sheet": "draftwright.sheet_dsl",
|
|
33
34
|
"fix_svg_page_size": "draftwright.export",
|
|
34
35
|
"lint_feature_coverage": "draftwright.linting",
|
|
35
36
|
"PmiRecord": "draftwright.pmi",
|
|
@@ -78,6 +79,7 @@ if TYPE_CHECKING: # static analysers / IDEs — no runtime import, no kernel co
|
|
|
78
79
|
from draftwright.linting import lint_feature_coverage
|
|
79
80
|
from draftwright.pmi import PmiRecord, extract_pmi
|
|
80
81
|
from draftwright.sheet import choose_scale
|
|
82
|
+
from draftwright.sheet_dsl import Sheet
|
|
81
83
|
|
|
82
84
|
|
|
83
85
|
def __dir__():
|
|
@@ -91,6 +93,7 @@ __all__ = [
|
|
|
91
93
|
"Drawing",
|
|
92
94
|
"FeatureInfo",
|
|
93
95
|
"PmiRecord",
|
|
96
|
+
"Sheet",
|
|
94
97
|
"analyse_face_levels",
|
|
95
98
|
"build_drawing",
|
|
96
99
|
"choose_scale",
|
|
@@ -30,6 +30,7 @@ from build123d_drafting.helpers import (
|
|
|
30
30
|
format_drawing_scale,
|
|
31
31
|
)
|
|
32
32
|
|
|
33
|
+
from draftwright.fits import FitClass
|
|
33
34
|
from draftwright.fonts import PLEX_MONO, PLEX_SANS_CONDENSED
|
|
34
35
|
from draftwright.layout import _greedy_strip_1d, _solve_strip_1d
|
|
35
36
|
|
|
@@ -86,6 +87,30 @@ def _fmt(v: float) -> str:
|
|
|
86
87
|
return str(r) if abs(v - r) < 1e-6 else f"{v:.1f}"
|
|
87
88
|
|
|
88
89
|
|
|
90
|
+
def _tol_suffix(tolerance, draft) -> str:
|
|
91
|
+
"""The ``±`` / limit tolerance suffix to append to a **callout** label (a ø leader
|
|
92
|
+
or a hole callout), matching byte-for-byte what ``Dimension(tolerance=…)`` renders
|
|
93
|
+
on a linear dim (helpers ``_format_label``): a symmetric ``float`` → ``" ±t"``; an
|
|
94
|
+
``(lower, upper)`` pair → ``" +upper -lower"`` — all rounded to the draft precision.
|
|
95
|
+
|
|
96
|
+
draftwright owns this suffix ONLY because the pinned helpers' ``Leader`` /
|
|
97
|
+
``HoleCallout`` take no ``tolerance=`` yet, so we bake it into the label string
|
|
98
|
+
ourselves. Delete this once helpers grows a first-class tolerance parameter for
|
|
99
|
+
those two (extraction tracked as #449) — ``Dimension`` formats its own (#28 / P2a).
|
|
100
|
+
|
|
101
|
+
A resolved fit (:class:`~draftwright.fits.FitClass`, P2a.2) renders its own class-code
|
|
102
|
+
or deviation suffix — it rides the same ``tolerance`` field as an aspect marker."""
|
|
103
|
+
if tolerance is None:
|
|
104
|
+
return ""
|
|
105
|
+
if isinstance(tolerance, FitClass):
|
|
106
|
+
return tolerance.suffix()
|
|
107
|
+
prec = draft.decimal_precision
|
|
108
|
+
if isinstance(tolerance, (int, float)):
|
|
109
|
+
return f" ±{round(tolerance, prec):.{prec}f}"
|
|
110
|
+
lo, hi = tolerance
|
|
111
|
+
return f" +{round(hi, prec):.{prec}f} -{round(lo, prec):.{prec}f}"
|
|
112
|
+
|
|
113
|
+
|
|
89
114
|
def _tag_sequence(n):
|
|
90
115
|
"""``A, B, …, Z, AA, AB, …`` — deterministic hole-table tags for *n* rows."""
|
|
91
116
|
tags = []
|
|
@@ -162,6 +187,15 @@ def _dim(p1, p2, side, distance, draft, **kwargs):
|
|
|
162
187
|
return d
|
|
163
188
|
|
|
164
189
|
|
|
190
|
+
# Dimension-line spacing (page-mm, scale-independent), the single source of truth for
|
|
191
|
+
# BOTH the ADR 0009 strip carve (via the `Strip` dataclass defaults below) and the
|
|
192
|
+
# sheet.py halo/depth estimates that must reserve the same space. Per ISO 129-1 / ASME
|
|
193
|
+
# Y14.5, the FIRST dimension line sits furthest from the outline (clears the outline +
|
|
194
|
+
# extension-line origins) and subsequent parallel lines stack tighter and uniform (#347).
|
|
195
|
+
_STRIP_GAP = 10.0 # clearance between the view outline and the first dimension line
|
|
196
|
+
_STRIP_SPACING = 2.5 # clear gap between successive parallel dimension lines (beyond the label)
|
|
197
|
+
|
|
198
|
+
|
|
165
199
|
@dataclass
|
|
166
200
|
class Strip:
|
|
167
201
|
"""A one-dimensional annotation band adjacent to an orthographic view.
|
|
@@ -184,8 +218,8 @@ class Strip:
|
|
|
184
218
|
anchor: float
|
|
185
219
|
outer_limit: float
|
|
186
220
|
direction: float = 1.0
|
|
187
|
-
gap: float =
|
|
188
|
-
spacing: float =
|
|
221
|
+
gap: float = _STRIP_GAP
|
|
222
|
+
spacing: float = _STRIP_SPACING
|
|
189
223
|
|
|
190
224
|
@property
|
|
191
225
|
def available(self) -> float:
|
|
@@ -220,7 +254,6 @@ _SLOT_DIM_DEPTH = 2 * _FONT_SIZE + _PAD # sv_zones.below: overall depth dimensi
|
|
|
220
254
|
_SLOT_DIM_HEIGHT = 2 * _FONT_SIZE + 2 * _PAD # fv_zones.right: overall height dim
|
|
221
255
|
|
|
222
256
|
|
|
223
|
-
_STRIP_SPACING = 4.0 # page-mm between successive annotations in a strip
|
|
224
257
|
_MIN_VIEW_MM = (
|
|
225
258
|
10.0 # min projected view dimension; below it annotation geometry degenerates (#129)
|
|
226
259
|
)
|
|
@@ -572,8 +605,11 @@ def _iso_bbox(dwg):
|
|
|
572
605
|
# Relocated from make_drawing for the sheet.py split (#162). Shared by sheet.py
|
|
573
606
|
# (choose_scale/_layout_geometry) and make_drawing's repack pass, so they live
|
|
574
607
|
# here in the shared base to keep the DAG acyclic.
|
|
575
|
-
|
|
576
|
-
_STRIP_GAP
|
|
608
|
+
# The base inter-view corridor: one first-line gap + one dimension tier. Tracks
|
|
609
|
+
# _STRIP_GAP so widening the first-line gap (#347) keeps the below-plan / between-view
|
|
610
|
+
# corridors from razor-fitting the first dim line (the #130 slack guarantee): 10 + 10.
|
|
611
|
+
_DIM_PAD = _STRIP_GAP + _SLOT_DIM_HEIGHT # 20.0
|
|
612
|
+
# _STRIP_GAP / _STRIP_SPACING are defined above (beside the `Strip` dataclass they seed).
|
|
577
613
|
|
|
578
614
|
_PAGE_SIZES = {
|
|
579
615
|
"A4": (297.0, 210.0),
|
|
@@ -179,6 +179,26 @@ _STEP_MIN_AREA_FRAC = 0.01
|
|
|
179
179
|
# ---------------------------------------------------------------------------
|
|
180
180
|
|
|
181
181
|
|
|
182
|
+
def _solids_body(part, src: str = "part"):
|
|
183
|
+
"""The part reduced to just its solids — the geometry the drawing is *of*.
|
|
184
|
+
|
|
185
|
+
AP242 STEP files (and hand-built Compounds) can carry non-solid geometry beside
|
|
186
|
+
the solid — PMI presentation wires, leader curves, construction edges/sketches —
|
|
187
|
+
which, left in, draw as phantom rectangles in every view and inflate the bounding
|
|
188
|
+
box, corrupting the scale choice and the envelope dimensions. Shared by
|
|
189
|
+
:func:`_analyse` and :meth:`draftwright.Sheet.model` (#453) so the model a caller
|
|
190
|
+
*inspects* is wrapped from the exact same body the engine *draws*."""
|
|
191
|
+
solids = part.solids()
|
|
192
|
+
if not solids:
|
|
193
|
+
return part
|
|
194
|
+
body = solids[0] if len(solids) == 1 else Compound(children=list(solids))
|
|
195
|
+
if body.bounding_box().size != part.bounding_box().size or len(part.edges()) != len(
|
|
196
|
+
body.edges()
|
|
197
|
+
):
|
|
198
|
+
_log.info("Dropping non-solid geometry from %s (PMI presentation data)", src)
|
|
199
|
+
return body
|
|
200
|
+
|
|
201
|
+
|
|
182
202
|
def _analyse(
|
|
183
203
|
step_file, title, number, tolerance, drawn_by, out, scale=None, page=None, pmi="off"
|
|
184
204
|
) -> Analysis:
|
|
@@ -192,22 +212,7 @@ def _analyse(
|
|
|
192
212
|
else:
|
|
193
213
|
part = _import_step(step_file)
|
|
194
214
|
src = str(step_file)
|
|
195
|
-
|
|
196
|
-
# border wires, leader curves) beside the solid; left in, it draws as
|
|
197
|
-
# phantom rectangles in every view and inflates the bounding box —
|
|
198
|
-
# corrupting the scale choice and the envelope dimensions. The drawing
|
|
199
|
-
# is of the solids.
|
|
200
|
-
solids = part.solids()
|
|
201
|
-
if solids:
|
|
202
|
-
body = solids[0] if len(solids) == 1 else Compound(children=list(solids))
|
|
203
|
-
if body.bounding_box().size != part.bounding_box().size or len(part.edges()) != len(
|
|
204
|
-
body.edges()
|
|
205
|
-
):
|
|
206
|
-
_log.info(
|
|
207
|
-
"Dropping non-solid geometry from %s (PMI presentation data)",
|
|
208
|
-
src,
|
|
209
|
-
)
|
|
210
|
-
part = body
|
|
215
|
+
part = _solids_body(part, src)
|
|
211
216
|
|
|
212
217
|
# Semantic PMI extraction (AP242 only; separate read-only pass).
|
|
213
218
|
pmi_records: list = []
|
|
@@ -302,6 +302,9 @@ class CorridorCandidate:
|
|
|
302
302
|
dedup: tuple | None = None
|
|
303
303
|
precedence: int = 0
|
|
304
304
|
force: bool = False
|
|
305
|
+
# The source IR feature this dim was rendered for — recorded as provenance when the
|
|
306
|
+
# dim is placed at drain (ADR 0010). ``None`` leaves the annotation feature-less.
|
|
307
|
+
feature: object | None = None
|
|
305
308
|
|
|
306
309
|
|
|
307
310
|
def solve_corridor(dwg, strip, view, axis, cands, tier):
|
|
@@ -353,13 +356,16 @@ def solve_corridor(dwg, strip, view, axis, cands, tier):
|
|
|
353
356
|
_promote_losers(c)
|
|
354
357
|
return
|
|
355
358
|
pairs = [(c.name, c.build) for c in kept]
|
|
356
|
-
|
|
359
|
+
feats = {c.name: c.feature for c in kept if c.feature is not None} # provenance (ADR 0010)
|
|
360
|
+
left = {
|
|
361
|
+
n for n, _ in place_strip_candidates(dwg, strip, view, axis, pairs, tier, features=feats)
|
|
362
|
+
}
|
|
357
363
|
force_pairs = [(c.name, c.build) for c in kept if c.name in left and c.force]
|
|
358
364
|
still = (
|
|
359
365
|
{
|
|
360
366
|
n
|
|
361
367
|
for n, _ in place_strip_candidates(
|
|
362
|
-
dwg, strip, view, axis, force_pairs, tier, force=True
|
|
368
|
+
dwg, strip, view, axis, force_pairs, tier, force=True, features=feats
|
|
363
369
|
)
|
|
364
370
|
}
|
|
365
371
|
if force_pairs
|
|
@@ -393,7 +399,7 @@ def drain_corridors(dwg):
|
|
|
393
399
|
dwg._corridor_batch = {}
|
|
394
400
|
|
|
395
401
|
|
|
396
|
-
def place_strip_candidates(dwg, strip, view, axis, cands, tier, *, force=False):
|
|
402
|
+
def place_strip_candidates(dwg, strip, view, axis, cands, tier, *, force=False, features=None):
|
|
397
403
|
"""Collect-then-solve placement of location/feature dims on one strip (ADR 0009).
|
|
398
404
|
The single shared strip placer that retires the ``Strip.allocate`` cursor (#150,
|
|
399
405
|
P3): each candidate in *cands* — an ``(name, build(pos)->dim)`` pair — is spaced by
|
|
@@ -481,7 +487,9 @@ def place_strip_candidates(dwg, strip, view, axis, cands, tier, *, force=False):
|
|
|
481
487
|
if not force and _box_hits(_geom_box(dim), blockers): # corridor crosses a leader
|
|
482
488
|
todo.append((name, build))
|
|
483
489
|
continue
|
|
484
|
-
|
|
490
|
+
# Record feature provenance (ADR 0010): the drain-time seam for corridor-placed
|
|
491
|
+
# dims — `features` maps this batch's names to their source IR feature.
|
|
492
|
+
dwg.add(dim, name, view=view, feature=(features or {}).get(name))
|
|
485
493
|
return todo
|
|
486
494
|
|
|
487
495
|
|