draftwright 0.3.2__tar.gz → 0.3.4__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.3.2 → draftwright-0.3.4}/CHANGELOG.md +302 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/PKG-INFO +41 -11
- {draftwright-0.3.2 → draftwright-0.3.4}/README.md +40 -10
- draftwright-0.3.4/docs/adr/README.md +51 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/pyproject.toml +1 -1
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/__init__.py +0 -9
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/_core.py +186 -30
- draftwright-0.3.4/src/draftwright/_geometry.py +142 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/analysis.py +11 -2
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/annotations/_common.py +507 -96
- draftwright-0.3.4/src/draftwright/annotations/balloons.py +242 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/annotations/from_model.py +689 -328
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/annotations/holes.py +42 -17
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/annotations/orchestrator.py +327 -160
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/annotations/sections.py +81 -1
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/builder.py +178 -54
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/cli.py +24 -3
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/compose.py +18 -47
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/drawing.py +507 -615
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/intents.py +5 -4
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/layout.py +114 -48
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/linting/__init__.py +2 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/linting/coverage.py +36 -5
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/linting/structural.py +216 -207
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/model/__init__.py +10 -7
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/model/declare.py +124 -47
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/model/detect.py +31 -7
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/model/ir.py +13 -6
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/model/planner.py +38 -3
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/projection.py +7 -3
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/recognition/__init__.py +19 -5
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/recognition/_features.py +1 -1
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/recognition/chamfers.py +50 -24
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/recognition/countersinks.py +19 -5
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/recognition/fillets.py +23 -13
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/recognition/flats.py +6 -3
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/recognition/grooves.py +22 -9
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/recognition/turned.py +5 -2
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/registry.py +16 -0
- draftwright-0.3.4/src/draftwright/repair.py +198 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/score.py +13 -9
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/sheet.py +61 -52
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/sheet_dsl.py +1 -1
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/sheet_emit.py +57 -5
- draftwright-0.3.4/tests/fixtures/grm03_thumbwheel_drive_screw.step +745 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/refactor_golden/centered_rebate.json +3 -3
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/refactor_golden/grid_plate.json +6 -6
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/refactor_golden/hex_bar.json +23 -5
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/refactor_golden/prismatic_ladder.json +3 -3
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/refactor_golden/scattered_plate.json +27 -27
- draftwright-0.3.4/tests/test_architecture_docs.py +43 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_carve_free_position_callers.py +22 -20
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_declare.py +129 -14
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_detect_once.py +37 -0
- draftwright-0.3.4/tests/test_drawing_encapsulation.py +370 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_e2e_slice.py +2 -2
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_e2e_standards.py +10 -7
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_fits.py +1 -1
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_gdt_placement.py +17 -17
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_import_boundaries.py +16 -1
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_layout.py +1 -34
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_layout_cleanliness.py +82 -58
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_lint_structural.py +64 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_make_drawing.py +701 -294
- draftwright-0.3.4/tests/test_occupancy_boxes.py +39 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_part_model.py +24 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_pmi.py +8 -8
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_private_test_imports.py +10 -1
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_recogniser_contract.py +18 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_refactor_golden.py +1 -1
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_registry.py +4 -4
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_render_seam.py +1 -1
- draftwright-0.3.4/tests/test_script_detail_parity.py +185 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_sheet_emit.py +148 -10
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_sheet_gdt.py +5 -5
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_sheet_notes.py +18 -4
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_sheet_of.py +1 -1
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_sheet_tables.py +34 -8
- draftwright-0.3.4/tests/test_solve_trace.py +275 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_strip_layout.py +82 -66
- draftwright-0.3.4/tests/test_tolerances.py +890 -0
- draftwright-0.3.4/tests/test_witness_label_reconciliation.py +56 -0
- draftwright-0.3.2/src/draftwright/_geometry.py +0 -50
- draftwright-0.3.2/src/draftwright/repair.py +0 -91
- draftwright-0.3.2/tests/test_drawing_encapsulation.py +0 -193
- draftwright-0.3.2/tests/test_tolerances.py +0 -234
- {draftwright-0.3.2 → draftwright-0.3.4}/.gitignore +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/LICENSE +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/skills/SKILL.md +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/annotate.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/annotations/__init__.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/export.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/fits.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/fonts/IBMPlexMono-Regular.ttf +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/fonts/IBMPlexSansCondensed-Regular.ttf +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/fonts/LICENSE-IBMPlexMono-OFL.txt +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/fonts/LICENSE-IBMPlexSansCondensed-OFL.txt +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/fonts/__init__.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/linting/issues.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/linting/suggest.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/make_drawing.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/pmi.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/recognition/_record.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/recognition/levels.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/recognition/plates.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/src/draftwright/recognition/slots.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/_kernel.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/_layout_sig.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/fixtures/nist_ctc_01_asme1_ap203.stp +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/fixtures/nist_ctc_01_asme1_ap242.stp +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/fixtures/nist_ctc_02_asme1_ap203.stp +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/fixtures/nist_ctc_02_asme1_ap242.stp +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/fixtures/nist_ctc_03_asme1_ap203.stp +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/fixtures/nist_ctc_03_asme1_ap242.stp +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/fixtures/nist_ctc_04_asme1_ap203.stp +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/fixtures/nist_ctc_04_asme1_ap242.stp +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/fixtures/nist_ctc_05_asme1_ap203.stp +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/fixtures/nist_ctc_05_asme1_ap242.stp +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/refactor_golden/bracket_section.json +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/refactor_golden/chamfered.json +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/refactor_golden/filleted.json +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/refactor_golden/flange_dense.json +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/refactor_golden/grooved_shaft.json +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/refactor_golden/holed_slot.json +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/refactor_golden/pocketed.json +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/refactor_golden/side_drilled.json +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/refactor_golden/turned_stepped.json +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_export_dxf_zoom.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_fillets_adjacency.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_layout_hypothesis.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_layout_property.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_lint_box_cache.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_lint_reconciliation.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_linting.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_object_aspects.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_pitch_dim_footprint.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_recognition.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_score.py +0 -0
- {draftwright-0.3.2 → draftwright-0.3.4}/tests/test_turned_steps.py +0 -0
|
@@ -1,5 +1,307 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## v0.3.4 — 2026-07-20
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- **Imperative `--script` output defaults to PDF and the modern export form**
|
|
10
|
+
(#709). **Breaking** for regenerated imperative scripts: without `-f`, a
|
|
11
|
+
regenerated script now writes `<stem>.pdf` (was SVG+DXF via the deprecated
|
|
12
|
+
legacy tuple), and the script-visible variables changed from
|
|
13
|
+
`svg_path, dxf_path` to a `paths` dict printed in the requested-format order.
|
|
14
|
+
This aligns the imperative flavour with the Sheet flavour and the direct CLI,
|
|
15
|
+
which have defaulted to PDF since #702/#288. Migration: regenerate with
|
|
16
|
+
`--format svg,dxf` to keep the old outputs. Previously-generated scripts are
|
|
17
|
+
unaffected (they carry their own export lines).
|
|
18
|
+
- **`finalize()` drains in the auto-pass's order** (#699 slice b): both build
|
|
19
|
+
paths now execute the orchestrator's one canonical `_PASS_SEQUENCE` (via the
|
|
20
|
+
shared `run_stages`), removing the hand-mirrored second orchestration in
|
|
21
|
+
`Drawing._drain_intents`. For the deferred/finalize path this reorders three
|
|
22
|
+
things toward auto-pass parity: the turned diameter/step-length set-solves now
|
|
23
|
+
place *before* the corridor drain (matching the auto-pass's obstacle
|
|
24
|
+
visibility); slots register after them; and the register-only height-ladder /
|
|
25
|
+
step-position stages register *after* locations — observable too, since
|
|
26
|
+
registration order decides corridor key-creation (= drain) order and
|
|
27
|
+
same-priority tie-breaks (Codex review; the new order is exactly the
|
|
28
|
+
auto-pass's). Recompose output may shift accordingly; the auto-pass order is
|
|
29
|
+
unchanged.
|
|
30
|
+
- **`Sheet.export` speaks the modern export API** (#702): it now takes
|
|
31
|
+
`formats=` (any of svg/dxf/pdf/png, default **PDF** — matching the CLI
|
|
32
|
+
default) and `dpi=`, and returns the `{format: path}` dict. Previously it
|
|
33
|
+
rode the deprecated legacy path, could only write SVG+DXF — the flagship
|
|
34
|
+
facade couldn't produce a PDF — and returned the old `(svg_path, dxf_path)`
|
|
35
|
+
tuple. **Breaking** for callers unpacking that tuple: use
|
|
36
|
+
`sheet.export(stem, formats=("svg", "dxf"))` and read the dict.
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
|
|
40
|
+
- **Solve-trace / explain mode** (#736, from the #733 post-mortem): opt-in
|
|
41
|
+
observability for strip placement. `build_drawing(trace=…)` or
|
|
42
|
+
`DRAFTWRIGHT_TRACE=<path-or-dir>` writes ONE JSON file per build (schema v2)
|
|
43
|
+
with two record types: `solves` — every corridor solve's key, strip bounds,
|
|
44
|
+
candidate set, the obstacles that carved the strip (with owning annotation
|
|
45
|
+
names), the free segments, and each candidate's outcome (placed /
|
|
46
|
+
dropped-with-reason / deduped / promoted / deferred-to-post-drain) — and
|
|
47
|
+
`pass_events` — the standalone strip passes plus the *immediate* placers
|
|
48
|
+
(post-drain machined-feature leader callouts, the turned diameter row/column
|
|
49
|
+
and step-length set-solves), each with per-item placed/dropped outcomes. So
|
|
50
|
+
"why did X drop" is one `jq` query (`.solves[].outcomes[]` /
|
|
51
|
+
`.pass_events[].items[]`). A `placement_unsatisfiable` strip-full drop now
|
|
52
|
+
also names the top occupants in its lint message (`…strip full; occupied
|
|
53
|
+
by: …`). Recording-only: an unwritable path logs a warning (never aborts a
|
|
54
|
+
build), writes are atomic, and the recorder joins `finalize()`'s #647
|
|
55
|
+
rollback. Default off; zero output change, nil cost when off.
|
|
56
|
+
|
|
57
|
+
- **`draftwright.model.authored_dimension`** (#704): the IR constructor behind
|
|
58
|
+
`Sheet.dimension()`, extracted so `build_drawing(model=…)` callers can author
|
|
59
|
+
a pre-measured drafting dimension without the `Sheet` façade.
|
|
60
|
+
|
|
61
|
+
### Added
|
|
62
|
+
|
|
63
|
+
- **Object-reference script workflow: docs + an inline tip** (#770/#771): a
|
|
64
|
+
generated `Sheet` script from a **live-source** input (a `module:attr` /
|
|
65
|
+
`file.py:attr` object spec, or a build123d object) now carries an inline tip
|
|
66
|
+
showing how to swap each detected-numbers line for a reference to your object
|
|
67
|
+
(ADR 0011 declare — the size is read off the object). A STEP-sourced script is
|
|
68
|
+
unchanged (byte-stable — no object to reference). The README documents the full
|
|
69
|
+
from-a-part-to-an-object-referenced-script workflow.
|
|
70
|
+
- **Knurl callout verb** (#765): `sheet.diameter(shaft).knurl("0.8")` →
|
|
71
|
+
`KNURL 0.8 STRAIGHT` (or `.knurl("0.8", "DIAMOND")`). Named sugar over
|
|
72
|
+
`.note()` with canonical formatting — a knurl is a text callout on a leader,
|
|
73
|
+
not modelled geometry, so it flows through the existing note path (no new
|
|
74
|
+
IR/render).
|
|
75
|
+
- **Standing ISO 7200 title-block fields** (#766): `material`, `date`, `revision`,
|
|
76
|
+
and `company` (legal owner) are now first-class — settable on `build_drawing` /
|
|
77
|
+
`make_drawing`, the `Sheet(...)` constructor, and the CLI (`--material` /
|
|
78
|
+
`--date` / `--revision` / `--company`), and emitted into the generated `Sheet`
|
|
79
|
+
script when non-default so a re-run reproduces them. Defaults preserve the prior
|
|
80
|
+
output (revision `A`, the rest blank). The `revision`/`legal_owner` cells the
|
|
81
|
+
renderer previously hardcoded now flow from these. (The imperative `--script`
|
|
82
|
+
flavour's title-block round-trip of these fields is a follow-up.)
|
|
83
|
+
- **First-class thread/tap callout on a hole** (#764): ``sheet.hole(...).thread("M3x0.5")``
|
|
84
|
+
(and ``declare.hole(..., thread=)``) folds a tap/thread spec onto the hole's existing
|
|
85
|
+
compound leader (e.g. ``ø2.5 THRU M3x0.5``) — a structured aspect that round-trips, so
|
|
86
|
+
``.thread(...).finish(...)`` yields Ra-on-thread. A declaration-only aspect (ADR 0011
|
|
87
|
+
side-layer: threads are cosmetic, not modelled geometry — no recogniser). The layout
|
|
88
|
+
width estimator (``_est_planned_bore_callout_width``) now accounts for the thread text so
|
|
89
|
+
the wider callout is reserved room and not dropped (the #261 estimator/render agreement).
|
|
90
|
+
|
|
91
|
+
### Fixed
|
|
92
|
+
|
|
93
|
+
- **Generated imperative script reconstructs side-drilled hole locations**
|
|
94
|
+
(#426/#133): an X/Y-axis (side-drilled) bore gets `dim_loc_*` position dims in
|
|
95
|
+
the direct build, but the `--script` emitter emitted a gap comment instead of a
|
|
96
|
+
`locate()` for non-Z holes (because `locate()`/`render_locations` are Z-plan
|
|
97
|
+
only, #133). The emitter now emits `dwg.locate(f)` for holes on any axis;
|
|
98
|
+
`finalize()` routes by the feature's axis — Z-plan holes drain through
|
|
99
|
+
`render_locations` as before, side-drilled bores through the whole-model
|
|
100
|
+
`_locate_off_axis_holes` pass (registered at the auto-pass's own
|
|
101
|
+
`off_axis_across`/`off_axis_along` stages, placed at the shared drain). The
|
|
102
|
+
Z-only `locate()` live contract is untouched (side-drilled locates never reach
|
|
103
|
+
it — they route to the off-axis stages). Closes the last direct-vs-generated
|
|
104
|
+
script parity gap tracked by #426.
|
|
105
|
+
- **Generated imperative script reconstructs rotational furniture** (#426/#424):
|
|
106
|
+
a turned/cylindrical part's `dwg.model()` carries a `RotationalFeature`, but the
|
|
107
|
+
`--script` emitter parked it in the gap-comment list, so an executed
|
|
108
|
+
reconstruction of e.g. a plain cylinder was missing `dim_od` and the
|
|
109
|
+
`centerline_front`/`centerline_side` marks the direct build draws. A new
|
|
110
|
+
`Drawing.rotational(feature)` add-verb records the intent, and `finalize()`
|
|
111
|
+
drains it through the shared whole-model `render_rotational` at the auto-pass's
|
|
112
|
+
own `"rotational"` stage — byte-identical to the direct build (no `only=` subset,
|
|
113
|
+
fixed literal names, so no naming-seam or `⊇`-vs-`==` gap). The emitter now emits
|
|
114
|
+
`dwg.rotational(f)` for rotational features. Closes the rotational half of the
|
|
115
|
+
direct-vs-generated-script convergence.
|
|
116
|
+
- **Emitted `Sheet` script now faithfully reproduces the direct CLI drawing**
|
|
117
|
+
(#707): the divergence reported against 0.3.3 on the Maquetto GRM-03 part was
|
|
118
|
+
closed by #709 (script `--format` forwarding) + #661 (finalize detail drain).
|
|
119
|
+
Locked with a vendored-fixture regression that asserts the full invariant the
|
|
120
|
+
issue named — same views, annotation inventory, page, scale **and** lint
|
|
121
|
+
between `build_drawing(...)` and the executed emitted `Sheet` — not just the
|
|
122
|
+
annotation signature the synthetic parity cases check.
|
|
123
|
+
- **`_largest_empty_rect` no longer blows up on crowded detail views** (#661):
|
|
124
|
+
the largest-empty-rectangle placer enumerated every candidate rectangle
|
|
125
|
+
(O(N⁴) in the coordinate-cut count) — fine for the iso view's handful of
|
|
126
|
+
obstacles, but the detail-view placer feeds it *every* placed-annotation
|
|
127
|
+
footprint, so a two-detail turned part reached ~70 obstacles and spent ~13 s
|
|
128
|
+
in a single call (multi-minute on the slower Windows CI runners, which tipped
|
|
129
|
+
some jobs over their wall-clock budget). It now skips candidates that provably
|
|
130
|
+
can't beat the best found so far (a `bisect` past every too-narrow pair, an
|
|
131
|
+
early `break` once even the widest is too small): the same exact result and
|
|
132
|
+
tie-break — verified identical to the naïve search across 3 000 random cases —
|
|
133
|
+
at ~85× the speed on the pathological input (12.6 s → 0.15 s).
|
|
134
|
+
- **Detail-view height demotion is pin- and provenance-safe** (#661, user
|
|
135
|
+
review): when a crowded prismatic detail needs the overall-height dim's room it
|
|
136
|
+
is demoted and the detail retried, but (a) the canonical `dim_height` name was
|
|
137
|
+
handed to that retry unconditionally — a *pinned* or user-replaced height could
|
|
138
|
+
be removed; it now passes the same pin/label guards as the generalised
|
|
139
|
+
`dim_length{n}` path (a pin, ADR 0012, is never demoted), and (b) if the retry
|
|
140
|
+
also failed, the restore re-added only object/name/view, orphaning the dim from
|
|
141
|
+
its envelope feature (lost `annotations_of`/`drop`/re-discovery); it now
|
|
142
|
+
snapshots and restores the feature provenance and pin too.
|
|
143
|
+
- **`finalize()` resolves queued detail requests — detail views now exist on the
|
|
144
|
+
edit path** (#661): the finalize drain was missing the auto pass's
|
|
145
|
+
`detail_request`/`details` stages, so a crowded turned head's queued
|
|
146
|
+
`DetailRequest` died with the per-run context and a prismatic
|
|
147
|
+
`detail_view=True` build never even queued one — generated scripts silently
|
|
148
|
+
lost every DETAIL A/B view. `build_drawing(detail_view=…)` is now persisted on
|
|
149
|
+
the build state so the drain gates the prismatic request exactly as the auto
|
|
150
|
+
pass does; the details stage re-projects the iso at sheet scale for the
|
|
151
|
+
free-rectangle search and refits it after (mirroring the auto pass's ordering,
|
|
152
|
+
where details place before the iso is fitted); and the overall-height demotion
|
|
153
|
+
retry now finds the height dim by its envelope attribution, not only the
|
|
154
|
+
auto-pass `dim_height` name. Three script-parity characterisation tests
|
|
155
|
+
(single detail, two details, turned head) flip from xfail to passing.
|
|
156
|
+
- **Y-turned stepped shafts no longer crash** (#661): `render_step_lengths`
|
|
157
|
+
projected every step span into the front view, where a Y-axis span is end-on
|
|
158
|
+
(a point) — `Dimension` then raised "start and end points must be different"
|
|
159
|
+
on BOTH the direct build and a generated script's finalize. Y-axis steps are
|
|
160
|
+
now skipped (no step render pipeline consumes Y, #731 — mirroring
|
|
161
|
+
`render_diameters`' x/z bucketing), the emitter flags the gap as a comment
|
|
162
|
+
instead of emitting an unreplayable `dimension()` verb, and a hand-written
|
|
163
|
+
Y step-length intent live-replays (surfacing the verb's own behaviour) rather
|
|
164
|
+
than vanishing into the routed no-op. The Y-axis script-parity
|
|
165
|
+
characterisation test flips from xfail to passing.
|
|
166
|
+
- **Boss heights are modeled, rendered, and coverage-checked** (#632): detected
|
|
167
|
+
and object-declared cylindrical bosses now carry their axial extent through
|
|
168
|
+
`BossFeature` and the dimension planner. Prismatic bosses receive a linear
|
|
169
|
+
height dimension in a profile view in addition to their end-on diameter
|
|
170
|
+
leader. `Drawing.lint()` reconciles that modeled height against the live
|
|
171
|
+
feature-owned `Dimension`, reporting `boss_height_missing` if it is removed
|
|
172
|
+
or otherwise absent (demoted to `info` on assembly drawings). This closes the
|
|
173
|
+
declarative false-negative where a boss's diameter and the overall envelope
|
|
174
|
+
were documented but its own independent height was not.
|
|
175
|
+
|
|
176
|
+
- **`--format` now reaches `--script` output** (#709, from the #702 adversarial
|
|
177
|
+
review): `--script -f svg` used to silently emit a PDF-producing script — the
|
|
178
|
+
CLI parsed the flag but never forwarded it. Both emitters now thread it
|
|
179
|
+
through: the Sheet script emits `sheet.export(stem, formats=(…,))` when
|
|
180
|
+
non-default (bare call for the PDF default), the imperative template embeds
|
|
181
|
+
the requested tuple and prints in the **requested** order, and
|
|
182
|
+
`generate_script` / `generate_sheet_script` grow a `formats=` parameter. The
|
|
183
|
+
dormant script-parity characterisation module
|
|
184
|
+
(`test_script_detail_parity.py`) is also un-skipped so its strict xfails give
|
|
185
|
+
the remaining gaps CI signal.
|
|
186
|
+
|
|
187
|
+
- **Authored fillet, flat, groove, pocket, plate and slot tolerances now render**
|
|
188
|
+
(#725/#726/#727/#728/#729/#730 / #698): the four leader-callout kinds join
|
|
189
|
+
`_CONVENTION` and their renderers (`render_fillets`/`render_flats`/
|
|
190
|
+
`render_grooves`/`render_pockets`) — and the plate-thickness and slot
|
|
191
|
+
width/length linear passes
|
|
192
|
+
(`render_plates`/`render_slots`, explicit `"linear"` entries) — consume the
|
|
193
|
+
planner's `DimensionGroup`s, binding each
|
|
194
|
+
planned dim explicitly by `(role, kind)` — so a `decorations`-authored ± /
|
|
195
|
+
limit tolerance reaches the placed callout (`R8 ±0.1`, `17 ±0.2 A/F`,
|
|
196
|
+
`4 ±0.1 WIDE × ø16 ±0.5`, `18 ±0.2 × 30 ±0.2 × 5 ±0.2 DEEP`, a `10 ±0.1`
|
|
197
|
+
thickness dim, an `8 ±0.1` slot width; on a
|
|
198
|
+
multi-value label each suffix rides its own number). Previously all six
|
|
199
|
+
passes formatted raw feature fields and silently dropped it — the latent #629
|
|
200
|
+
bug class the ADR 0015 bypass list documents. The fillet `n× R` and flat
|
|
201
|
+
double-D/hex collapses stay render-side (first-authored tolerance wins, the
|
|
202
|
+
`render_diameters` precedent); a pocket's three values — and a slot's
|
|
203
|
+
width + length — share one authored
|
|
204
|
+
tolerance (all kind `"length"`). The slot's model-derived datum position dim
|
|
205
|
+
and its corridor/immediate placement mechanics are untouched. Untolerated
|
|
206
|
+
labels/views/placement are unchanged.
|
|
207
|
+
- **Authored chamfer tolerances now render** (#724 / #698): `render_chamfers`
|
|
208
|
+
consumes the planner's `DimensionGroup` (chamfer joins `_CONVENTION` as a
|
|
209
|
+
leader), so a `decorations`-authored ± / limit tolerance on a chamfer leg
|
|
210
|
+
reaches the placed callout (`C12 ±0.2`). Previously the pass formatted raw
|
|
211
|
+
feature fields and silently dropped it — the latent #629 bug class the ADR
|
|
212
|
+
0015 bypass list documents. Untolerated chamfer labels/views are unchanged.
|
|
213
|
+
- **`model.chamfer(face=…)` rejects a non-planar face** (#704): the declared
|
|
214
|
+
front-end silently accepted a curved face (`normal_at()` does not fail on
|
|
215
|
+
one) and read garbage legs off it — e.g. a fillet or countersink face became
|
|
216
|
+
a bogus `ChamferFeature`. It now raises the documented `ValueError`, using
|
|
217
|
+
the recogniser's own planarity gate (`classify_bevel`), so declared and
|
|
218
|
+
detected chamfers classify identically by construction.
|
|
219
|
+
|
|
220
|
+
### Fixed
|
|
221
|
+
|
|
222
|
+
- **CTC-02/04 no longer drop step-height / overall-height dims** (#733): when
|
|
223
|
+
the height ladder joined the corridor solve (#689) it silently moved from
|
|
224
|
+
"places early" to "registers early, places at the drain" — so the immediate
|
|
225
|
+
machined-feature leader callouts (pockets on CTC-04) filled the front-right
|
|
226
|
+
strip first and the FORCED principal dims hard-dropped
|
|
227
|
+
(`placement_unsatisfiable`), a priority inversion the old stage ordering had
|
|
228
|
+
prevented implicitly. The leader-callout stages (chamfers/fillets/flats/
|
|
229
|
+
pockets) now sit **after the corridor drain** in `_PASS_SEQUENCE` (joining
|
|
230
|
+
grooves, which already placed post-drain for exactly this reason): principal
|
|
231
|
+
dims win by construction, and a callout with no clear room yields with a
|
|
232
|
+
warning, never a principal-dim error. Callout leaders may shift (they now
|
|
233
|
+
route around the drained dims); on the `hex_bar` golden this *recovers* a
|
|
234
|
+
silently starved `m_env_width` envelope dim.
|
|
235
|
+
|
|
236
|
+
### Removed
|
|
237
|
+
|
|
238
|
+
- **Three orphaned root exports** (#704): `draftwright.recognise_face_levels`
|
|
239
|
+
(one recogniser of ~12 — incoherent as a top-level surface; import it from
|
|
240
|
+
`draftwright.recognition`), `draftwright.dedup_diams` (an analysis internal),
|
|
241
|
+
and `draftwright.fix_svg_page_size` (still available from `draftwright.export`
|
|
242
|
+
and the `draftwright.make_drawing` facade). None had any internal or test
|
|
243
|
+
consumer via the root API.
|
|
244
|
+
|
|
245
|
+
### Deprecated
|
|
246
|
+
|
|
247
|
+
- **The seven `Drawing` compat aliases** (`_named`/`_anno_view`/`_pinned`/
|
|
248
|
+
`_build_issues`/`_pattern_callouts`/`_patterned_holes`/
|
|
249
|
+
`_dropped_callout_diams`) are formally on notice (#699 slice c, tracked by
|
|
250
|
+
#720): removal target **0.4.0**, per ADR 0005 §4. Read through the public
|
|
251
|
+
surface instead — `annotations()`, `iter_annotations()`, `get_annotation()`,
|
|
252
|
+
`view_of()`, `registry.pinned_names()`/`is_pinned()`, and the new
|
|
253
|
+
`registry.issues`. All external production and test call sites in this repo
|
|
254
|
+
are already redirected (`drawing.py` internals ride the aliases until the
|
|
255
|
+
#720 deletion).
|
|
256
|
+
- **`draftwright.sheet_dsl`** (belated announcement — the alias shim shipped
|
|
257
|
+
in 0.3.1 when #640 renamed the module to `draftwright.sheet`): importing it
|
|
258
|
+
warns; it will be removed in **0.4.0**. Import from `draftwright.sheet`, or
|
|
259
|
+
just `from draftwright import Sheet`.
|
|
260
|
+
|
|
261
|
+
## v0.3.3 — 2026-07-18
|
|
262
|
+
|
|
263
|
+
The **layout-quality release**: the occupancy model gets honest about ink shape,
|
|
264
|
+
the last solver-invisible pass joins the corridor solve, and crossed labels shift
|
|
265
|
+
themselves clear. Also completes the #635 consolidation epic (ADR 0005 fully
|
|
266
|
+
executed; ADR 0009's no-invisible-occupant guarantee now holds by construction
|
|
267
|
+
for every auto-pass).
|
|
268
|
+
|
|
269
|
+
### Added
|
|
270
|
+
|
|
271
|
+
- **Witness-crossing label shifts** (#690): a dimension label crossed by another
|
|
272
|
+
dimension's witness stroke now shifts along its own line to clear it — a
|
|
273
|
+
deterministic post-drain pass using the repair machinery (destination-aware,
|
|
274
|
+
axis-aligned only, pinned dims exempt).
|
|
275
|
+
|
|
276
|
+
### Changed
|
|
277
|
+
|
|
278
|
+
- **L-shaped occupancy** (#685): placement obstacles decompose into per-stroke
|
|
279
|
+
boxes + label box (helpers ≥0.14 `segments`) instead of one hull, with
|
|
280
|
+
preset-aware arrowhead pads. View-corner contention between perpendicular
|
|
281
|
+
strips disappears by construction (a plate-thickness dim returns to its
|
|
282
|
+
natural strip); packing tightens. The cleanliness ratchet mirrors the model —
|
|
283
|
+
only transverse stroke crossings are exempt, so parallel overprints and label
|
|
284
|
+
contacts are detected honestly (twelve hull-artifact allowlist entries burned
|
|
285
|
+
down; the legitimate shared-datum running-dimension pairs are documented).
|
|
286
|
+
- **Height ladder joins the corridor solve** (#636): step heights + overall
|
|
287
|
+
height are corridor candidates (the leapfrog witness cursor survives as a
|
|
288
|
+
deterministic build-time chain; the overall height stacks outermost by
|
|
289
|
+
construction and packs tighter). An explicitly-requested crowded-step detail
|
|
290
|
+
view now *deliberately* demotes the overall-height dim when the sheet cannot
|
|
291
|
+
hold both (previously the carve dropped it silently); a Y-drilled hole's Z
|
|
292
|
+
location joins the front running ladder per the documented routing.
|
|
293
|
+
- **One typed `BuildState`** (#639): the drawing's build context (analysis,
|
|
294
|
+
part model, lint geometry caches) consolidates into a single object filled at
|
|
295
|
+
one builder site, single-writer-guarded; the render passes make zero private
|
|
296
|
+
`Drawing` reads (fail-closed empty allowlist).
|
|
297
|
+
|
|
298
|
+
### Fixed
|
|
299
|
+
|
|
300
|
+
- A prismatic detail view no longer loses both the detail *and* the height dim
|
|
301
|
+
when space is tight (transactional demotion).
|
|
302
|
+
- The GD&T alternate-strip fallback defers until every corridor has drained, so
|
|
303
|
+
it can never preempt a sibling strip's reserved corner.
|
|
304
|
+
|
|
3
305
|
## v0.3.2 — 2026-07-18
|
|
4
306
|
|
|
5
307
|
The **performance release**: the #602 perf loop (six PRs) plus the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: draftwright
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Automated technical-drawing generation for build123d
|
|
5
5
|
Project-URL: Homepage, https://github.com/pzfreo/draftwright
|
|
6
6
|
Project-URL: Repository, https://github.com/pzfreo/draftwright
|
|
@@ -789,18 +789,47 @@ sheet.envelope()
|
|
|
789
789
|
sheet.datum("A", plate.faces().sort_by()[-1]) # datum A on the top face
|
|
790
790
|
hole = sheet.hole(bore).finish("1.6").note("M3x0.5 TAP") # ⌀8 bore, Ra 1.6, tapped
|
|
791
791
|
sheet.control(hole).position(0.1, to="A", diameter=True) # ⌀0.1 position wrt A
|
|
792
|
-
sheet.export("plate") # writes plate.
|
|
792
|
+
sheet.export("plate") # writes plate.pdf
|
|
793
793
|
```
|
|
794
794
|
|
|
795
795
|
Every aspect the geometry can't carry is a declared verb: `.tolerance(±)` / `.fit("H7")`
|
|
796
796
|
(ISO 286), `.finish("Ra")` (ISO 1302), `sheet.datum(letter, ref)` (ISO 5459), and
|
|
797
797
|
`sheet.control(ref)` with all 14 ISO 1101 characteristics
|
|
798
|
-
(`.position`/`.flatness`/`.perpendicularity`/`.circular_runout`/…), plus
|
|
799
|
-
/
|
|
798
|
+
(`.position`/`.flatness`/`.perpendicularity`/`.circular_runout`/…), plus `.thread("M3x0.5")`
|
|
799
|
+
(a tap/thread spec folded onto the hole callout), `.knurl("0.8")`, and `sheet.note(text, ref)`
|
|
800
|
+
/ `.note(text)` for any other free-text shop callout (`DEBURR`, chip-relief). A feature verb returns
|
|
800
801
|
a chainable handle (`sheet.hole(bore)`) that the aspect and `control(...)` verbs decorate;
|
|
801
802
|
targets are placed automatically — the view and strip are derived from the referenced
|
|
802
803
|
feature or face, with `view=`/`side=` overrides.
|
|
803
804
|
|
|
805
|
+
### From a part to an object-referenced script
|
|
806
|
+
|
|
807
|
+
`draftwright part.step --script` writes an editable `Sheet` script. When you built the part
|
|
808
|
+
yourself (rather than importing a STEP), pass your **live source** as a `module:attr` (or
|
|
809
|
+
`file.py:attr`) object spec, and the script binds `part` back to your real object:
|
|
810
|
+
|
|
811
|
+
```bash
|
|
812
|
+
draftwright mymodule:thumbwheel --script
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
The emitted values are *detected* off the geometry — honest, and a good starting point. Since
|
|
816
|
+
you have the objects, swap each numbered line for a reference so the object stays the single
|
|
817
|
+
source of truth (ADR 0011 — the size is read off the object, no numbers restated):
|
|
818
|
+
|
|
819
|
+
```python
|
|
820
|
+
# generated (detected):
|
|
821
|
+
sheet.step(diameter=8, length=25, at=(0, 0, 12), axis="z")
|
|
822
|
+
|
|
823
|
+
# edited to reference your objects:
|
|
824
|
+
features = mymodule.thumbwheel_features()
|
|
825
|
+
part = features.part
|
|
826
|
+
sheet.step(features.journal)
|
|
827
|
+
sheet.hole(features.m3_bore).thread("M3x0.5").finish("1.6") # tapped + Ra on the same hole
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
An object-sourced script carries an inline tip pointing at exactly this edit; a STEP-sourced
|
|
831
|
+
script keeps the detected numbers (there's no object to reference).
|
|
832
|
+
|
|
804
833
|
## What it produces
|
|
805
834
|
|
|
806
835
|
- **Three orthographic views** (front, plan, side) sized and scaled automatically to the
|
|
@@ -854,14 +883,16 @@ dwg.repair() # auto-fix mechanically-fixable lint; never worsens
|
|
|
854
883
|
|
|
855
884
|
Each `LintIssue` carries a domain-meaningful `code` and, when computable, a
|
|
856
885
|
ready-to-apply `suggestion`. See `docs/adr/` for the design (deterministic
|
|
857
|
-
generation, the lint→repair loop, and
|
|
886
|
+
generation, the lint→repair loop, and collect-then-solve placement).
|
|
858
887
|
|
|
859
888
|
## Architecture
|
|
860
889
|
|
|
861
|
-
draftwright is structured as a **part-drawing compiler** (ADR
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
890
|
+
draftwright is structured as a **part-drawing compiler** (ADR 0015): recognised
|
|
891
|
+
or declared features converge on a `PartModel` IR, then planner-fed and
|
|
892
|
+
sanctioned model-routed render intents feed shared placement, projection, and
|
|
893
|
+
export. Coverage lint independently compares recognised geometry with the
|
|
894
|
+
placed drawing, so an upstream omission cannot hide from verification. It
|
|
895
|
+
builds on two libraries:
|
|
865
896
|
|
|
866
897
|
```
|
|
867
898
|
draftwright
|
|
@@ -874,8 +905,7 @@ primitives (`Dimension`, `Leader`, etc.) live in `build123d-drafting-helpers` an
|
|
|
874
905
|
be used independently. The compiler is largely converged in production — turned
|
|
875
906
|
dims/lengths, centre marks, envelope, slots, holes (callouts/locations/grouping),
|
|
876
907
|
the section A–A trigger, the prismatic step-ladder + rotational furniture, and PMI/GD&T
|
|
877
|
-
are all on the IR
|
|
878
|
-
build → plan → render). See
|
|
908
|
+
are all on the IR. See
|
|
879
909
|
[`docs/target-architecture.md`](docs/target-architecture.md) and
|
|
880
910
|
[`docs/layout-algorithm-primer.md`](docs/layout-algorithm-primer.md) for a short walkthrough, plus
|
|
881
911
|
[`docs/adr/`](docs/adr/). The engine handles view layout (strip/zone model), scale
|
|
@@ -94,18 +94,47 @@ sheet.envelope()
|
|
|
94
94
|
sheet.datum("A", plate.faces().sort_by()[-1]) # datum A on the top face
|
|
95
95
|
hole = sheet.hole(bore).finish("1.6").note("M3x0.5 TAP") # ⌀8 bore, Ra 1.6, tapped
|
|
96
96
|
sheet.control(hole).position(0.1, to="A", diameter=True) # ⌀0.1 position wrt A
|
|
97
|
-
sheet.export("plate") # writes plate.
|
|
97
|
+
sheet.export("plate") # writes plate.pdf
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
Every aspect the geometry can't carry is a declared verb: `.tolerance(±)` / `.fit("H7")`
|
|
101
101
|
(ISO 286), `.finish("Ra")` (ISO 1302), `sheet.datum(letter, ref)` (ISO 5459), and
|
|
102
102
|
`sheet.control(ref)` with all 14 ISO 1101 characteristics
|
|
103
|
-
(`.position`/`.flatness`/`.perpendicularity`/`.circular_runout`/…), plus
|
|
104
|
-
/
|
|
103
|
+
(`.position`/`.flatness`/`.perpendicularity`/`.circular_runout`/…), plus `.thread("M3x0.5")`
|
|
104
|
+
(a tap/thread spec folded onto the hole callout), `.knurl("0.8")`, and `sheet.note(text, ref)`
|
|
105
|
+
/ `.note(text)` for any other free-text shop callout (`DEBURR`, chip-relief). A feature verb returns
|
|
105
106
|
a chainable handle (`sheet.hole(bore)`) that the aspect and `control(...)` verbs decorate;
|
|
106
107
|
targets are placed automatically — the view and strip are derived from the referenced
|
|
107
108
|
feature or face, with `view=`/`side=` overrides.
|
|
108
109
|
|
|
110
|
+
### From a part to an object-referenced script
|
|
111
|
+
|
|
112
|
+
`draftwright part.step --script` writes an editable `Sheet` script. When you built the part
|
|
113
|
+
yourself (rather than importing a STEP), pass your **live source** as a `module:attr` (or
|
|
114
|
+
`file.py:attr`) object spec, and the script binds `part` back to your real object:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
draftwright mymodule:thumbwheel --script
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The emitted values are *detected* off the geometry — honest, and a good starting point. Since
|
|
121
|
+
you have the objects, swap each numbered line for a reference so the object stays the single
|
|
122
|
+
source of truth (ADR 0011 — the size is read off the object, no numbers restated):
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
# generated (detected):
|
|
126
|
+
sheet.step(diameter=8, length=25, at=(0, 0, 12), axis="z")
|
|
127
|
+
|
|
128
|
+
# edited to reference your objects:
|
|
129
|
+
features = mymodule.thumbwheel_features()
|
|
130
|
+
part = features.part
|
|
131
|
+
sheet.step(features.journal)
|
|
132
|
+
sheet.hole(features.m3_bore).thread("M3x0.5").finish("1.6") # tapped + Ra on the same hole
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
An object-sourced script carries an inline tip pointing at exactly this edit; a STEP-sourced
|
|
136
|
+
script keeps the detected numbers (there's no object to reference).
|
|
137
|
+
|
|
109
138
|
## What it produces
|
|
110
139
|
|
|
111
140
|
- **Three orthographic views** (front, plan, side) sized and scaled automatically to the
|
|
@@ -159,14 +188,16 @@ dwg.repair() # auto-fix mechanically-fixable lint; never worsens
|
|
|
159
188
|
|
|
160
189
|
Each `LintIssue` carries a domain-meaningful `code` and, when computable, a
|
|
161
190
|
ready-to-apply `suggestion`. See `docs/adr/` for the design (deterministic
|
|
162
|
-
generation, the lint→repair loop, and
|
|
191
|
+
generation, the lint→repair loop, and collect-then-solve placement).
|
|
163
192
|
|
|
164
193
|
## Architecture
|
|
165
194
|
|
|
166
|
-
draftwright is structured as a **part-drawing compiler** (ADR
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
195
|
+
draftwright is structured as a **part-drawing compiler** (ADR 0015): recognised
|
|
196
|
+
or declared features converge on a `PartModel` IR, then planner-fed and
|
|
197
|
+
sanctioned model-routed render intents feed shared placement, projection, and
|
|
198
|
+
export. Coverage lint independently compares recognised geometry with the
|
|
199
|
+
placed drawing, so an upstream omission cannot hide from verification. It
|
|
200
|
+
builds on two libraries:
|
|
170
201
|
|
|
171
202
|
```
|
|
172
203
|
draftwright
|
|
@@ -179,8 +210,7 @@ primitives (`Dimension`, `Leader`, etc.) live in `build123d-drafting-helpers` an
|
|
|
179
210
|
be used independently. The compiler is largely converged in production — turned
|
|
180
211
|
dims/lengths, centre marks, envelope, slots, holes (callouts/locations/grouping),
|
|
181
212
|
the section A–A trigger, the prismatic step-ladder + rotational furniture, and PMI/GD&T
|
|
182
|
-
are all on the IR
|
|
183
|
-
build → plan → render). See
|
|
213
|
+
are all on the IR. See
|
|
184
214
|
[`docs/target-architecture.md`](docs/target-architecture.md) and
|
|
185
215
|
[`docs/layout-algorithm-primer.md`](docs/layout-algorithm-primer.md) for a short walkthrough, plus
|
|
186
216
|
[`docs/adr/`](docs/adr/). The engine handles view layout (strip/zone model), scale
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Architecture decision records
|
|
2
|
+
|
|
3
|
+
This is the front door to draftwright's ADR corpus. Start with the **Current
|
|
4
|
+
architecture** table; open retired or superseded records only for design history.
|
|
5
|
+
|
|
6
|
+
## Maintenance rules
|
|
7
|
+
|
|
8
|
+
- Keep the status header to one load-bearing statement.
|
|
9
|
+
- At roughly four amendments, write a successor ADR and freeze the old record.
|
|
10
|
+
The superseded file remains the historical why-trail; do not rewrite it.
|
|
11
|
+
- Cite symbols and test names, never source line numbers.
|
|
12
|
+
- If an invariant can be checked mechanically, guard it with a test. A claim
|
|
13
|
+
that something is machine-checked must name the real check.
|
|
14
|
+
- Preserve reversals in the frozen record. The successor compresses each to one
|
|
15
|
+
line and points back to the history.
|
|
16
|
+
- Update this index whenever an ADR is accepted, retired, or superseded, or when
|
|
17
|
+
its guarding test changes.
|
|
18
|
+
|
|
19
|
+
## Current architecture
|
|
20
|
+
|
|
21
|
+
| ADR | Title | Decision | Status | Representative guards |
|
|
22
|
+
| --- | --- | --- | --- | --- |
|
|
23
|
+
| [0001](0001-deterministic-generation-over-editable-dsl.md) | Deterministic generation and domain-semantic editing over a bespoke editable-code DSL | Prefer deterministic generation and domain-semantic editing over a primitive editable-code DSL. | Accepted | `test_sheet_emit.py`, `test_make_drawing.py` |
|
|
24
|
+
| [0002](0002-iterate-via-lint-critique-and-domain-repair.md) | Iterate via lint critique and domain-semantic repair, not by editing generated code | Refine drawings through machine-readable lint and narrowly allowlisted domain repair. | Accepted | `test_linting.py`, `test_lint_structural.py`, `test_make_drawing.py` |
|
|
25
|
+
| [0004](0004-compose-then-pack-view-blocks.md) | Compose-then-pack: views as blocks carrying their annotation footprint | Compose each view with its annotation footprint, then pack fixed-topology blocks. | Accepted | `test_layout.py`, `test_layout_cleanliness.py`, `test_refactor_golden.py` |
|
|
26
|
+
| [0005](0005-pipeline-architecture-and-state-ownership.md) | Compiler-pipeline module boundaries and single-owner build state | Give compiler stages explicit module homes and build-time state explicit owners. | Accepted; implemented; compatibility aliases tracked by #720 | `test_import_boundaries.py`, `test_drawing_encapsulation.py`, `test_registry.py` |
|
|
27
|
+
| [0006](0006-deterministic-layout-via-bundled-fonts.md) | Deterministic cross-platform layout via bundled, path-pinned fonts | Pin bundled font files so text measurement and layout are cross-platform deterministic. | Accepted | `test_refactor_golden.py`, `test_layout_cleanliness.py` |
|
|
28
|
+
| [0007](0007-own-recognition-and-linting.md) | draftwright owns feature recognition and linting; helpers becomes the rendering library | draftwright owns recognition and linting; helpers remains a rendering library. | Accepted | `test_import_boundaries.py`, `test_recognition.py`, `test_linting.py` |
|
|
29
|
+
| [0010](0010-annotation-provenance-seam.md) | Annotation provenance: record intent → annotation once, at the render seam | Record annotation provenance once at the render/add seam. | Accepted; landed | `test_render_seam.py`, `test_registry.py` |
|
|
30
|
+
| [0011](0011-ir-as-public-input.md) | The IR as a public input: declare features, don't only detect them | Accept the feature IR as public input through declarations and the `Sheet` façade. | Accepted; core landed; #62/#462/#495 remain | `test_declare.py`, `test_object_aspects.py`, `test_sheet_gdt.py` |
|
|
31
|
+
| [0012](0012-edits-as-pinned-priority-candidates-in-the-global-solve.md) | User annotation edits are pinned, priority-ranked corridor candidates | Drain recorded semantic edits through corridor placement with pin and priority. | Accepted; partially landed; full recomposition/parity remains #426/#707 (#661 detail views landed) | `test_make_drawing.py`, `test_sheet_emit.py` |
|
|
32
|
+
| [0013](0013-uniform-recognition-and-shared-package.md) | A uniform recogniser/feature contract (with `b123d-recognisers` as its deferred shared deployment) | Enforce a uniform geometry-only recogniser contract internally; defer package extraction until there is a second consumer. | Accepted; Phase 1 in progress; extraction deferred | `test_recogniser_contract.py`, `test_import_boundaries.py` |
|
|
33
|
+
| [0014](0014-collect-then-solve-annotation-placement.md) | Collect-then-solve annotation placement (as built) | Collect, select, assign, and deterministically solve annotations per corridor before rendering. | Accepted; supersedes 0009 | `test_carve_free_position_callers.py`, `test_strip_layout.py`, `test_layout_property.py`, `test_import_boundaries.py` |
|
|
34
|
+
| [0015](0015-part-drawing-compiler-as-built.md) | The part-drawing compiler, as built | Use one detected-or-declared feature IR and planner-fed dimension groups as the compiler waist. | Accepted; supersedes 0008 | `test_part_model.py`, `test_detect_once.py`, `test_import_boundaries.py` |
|
|
35
|
+
|
|
36
|
+
## Historical records
|
|
37
|
+
|
|
38
|
+
| ADR | Title | Historical decision | Status | Read instead |
|
|
39
|
+
| --- | --- | --- | --- | --- |
|
|
40
|
+
| [0003](0003-constraint-based-layout.md) | Constraint-based layout: one solver for every placeable | Explored a universal `Placeable`/`LayoutSolver` and page-global constraint solve. | Retired; carrier deleted and #94 closed as unnecessary | [0004](0004-compose-then-pack-view-blocks.md) for outer layout and [0014](0014-collect-then-solve-annotation-placement.md) for inner placement |
|
|
41
|
+
| [0008](0008-unified-feature-model-and-dimensioning-planner.md) | The part-drawing compiler: a Feature/DimParameter IR and a dimensioning planner | Introduced the feature/parameter IR and dimensioning-planner direction. | Superseded by 0015; frozen | [0015](0015-part-drawing-compiler-as-built.md) |
|
|
42
|
+
| [0009](0009-boundary-labeling-strip-placement.md) | Boundary labeling: collect-then-solve per-strip annotation placement | Developed collect-then-solve boundary-label placement through nine amendments. | Superseded by 0014; frozen | [0014](0014-collect-then-solve-annotation-placement.md) |
|
|
43
|
+
|
|
44
|
+
## Reading paths
|
|
45
|
+
|
|
46
|
+
- Compiler and state ownership: 0001 → 0005 → 0015.
|
|
47
|
+
- Recognition and public declaration: 0007 → 0013 → 0011 → 0015.
|
|
48
|
+
- Layout and placement: 0004 → 0014 → 0012.
|
|
49
|
+
- Quality and correction: 0002, with provenance from 0010.
|
|
50
|
+
|
|
51
|
+
Tracking issue: #745.
|
|
@@ -23,15 +23,12 @@ from typing import TYPE_CHECKING
|
|
|
23
23
|
# none of these names, so they stay sub-second instead of paying for the kernel
|
|
24
24
|
# on every TAB press (#313). Each name maps to the submodule that provides it.
|
|
25
25
|
_LAZY = {
|
|
26
|
-
"recognise_face_levels": "draftwright.recognition",
|
|
27
|
-
"dedup_diams": "draftwright.analysis",
|
|
28
26
|
"build_drawing": "draftwright.builder",
|
|
29
27
|
"generate_script": "draftwright.builder",
|
|
30
28
|
"make_drawing": "draftwright.builder",
|
|
31
29
|
"Drawing": "draftwright.drawing",
|
|
32
30
|
"FeatureInfo": "draftwright.drawing",
|
|
33
31
|
"Sheet": "draftwright.sheet",
|
|
34
|
-
"fix_svg_page_size": "draftwright.export",
|
|
35
32
|
"lint_feature_coverage": "draftwright.linting",
|
|
36
33
|
"PmiRecord": "draftwright.pmi",
|
|
37
34
|
"extract_pmi": "draftwright.pmi",
|
|
@@ -72,14 +69,11 @@ _sys.modules[__name__].__class__ = _DraftwrightModule
|
|
|
72
69
|
|
|
73
70
|
|
|
74
71
|
if TYPE_CHECKING: # static analysers / IDEs — no runtime import, no kernel cost
|
|
75
|
-
from draftwright.analysis import dedup_diams
|
|
76
72
|
from draftwright.builder import build_drawing, generate_script, make_drawing
|
|
77
73
|
from draftwright.compose import choose_scale
|
|
78
74
|
from draftwright.drawing import Drawing, FeatureInfo
|
|
79
|
-
from draftwright.export import fix_svg_page_size
|
|
80
75
|
from draftwright.linting import lint_feature_coverage
|
|
81
76
|
from draftwright.pmi import PmiRecord, extract_pmi
|
|
82
|
-
from draftwright.recognition import recognise_face_levels
|
|
83
77
|
from draftwright.sheet import Sheet
|
|
84
78
|
|
|
85
79
|
|
|
@@ -95,12 +89,9 @@ __all__ = [
|
|
|
95
89
|
"FeatureInfo",
|
|
96
90
|
"PmiRecord",
|
|
97
91
|
"Sheet",
|
|
98
|
-
"recognise_face_levels",
|
|
99
92
|
"build_drawing",
|
|
100
93
|
"choose_scale",
|
|
101
|
-
"dedup_diams",
|
|
102
94
|
"extract_pmi",
|
|
103
|
-
"fix_svg_page_size",
|
|
104
95
|
"generate_script",
|
|
105
96
|
"lint_feature_coverage",
|
|
106
97
|
"make_drawing",
|