draftwright 0.2.11__tar.gz → 0.2.13__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.11 → draftwright-0.2.13}/CHANGELOG.md +70 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/PKG-INFO +5 -5
- {draftwright-0.2.11 → draftwright-0.2.13}/README.md +4 -4
- {draftwright-0.2.11 → draftwright-0.2.13}/pyproject.toml +1 -1
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/analysis.py +44 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/annotations/_common.py +29 -3
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/annotations/from_model.py +140 -84
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/annotations/holes.py +106 -29
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/annotations/orchestrator.py +14 -31
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/builder.py +73 -23
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/drawing.py +288 -56
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/linting/suggest.py +13 -9
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/model/__init__.py +4 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/model/detect.py +28 -9
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/model/ir.py +53 -9
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/sheet.py +165 -69
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/sheet_dsl.py +73 -1
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/sheet_emit.py +90 -13
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/layout_snapshots/dshape.json +3 -3
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/layout_snapshots/side_drilled.json +3 -3
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_make_drawing.py +461 -22
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_pmi.py +30 -9
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_render_seam.py +34 -11
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_sheet_emit.py +160 -15
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_strip_layout.py +66 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_tolerances.py +16 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/.gitignore +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/LICENSE +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/skills/SKILL.md +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/__init__.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/_core.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/annotate.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/annotations/__init__.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/annotations/sections.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/cli.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/export.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/features.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/fits.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/fonts/IBMPlexMono-Regular.ttf +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/fonts/IBMPlexSansCondensed-Regular.ttf +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/fonts/LICENSE-IBMPlexMono-OFL.txt +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/fonts/LICENSE-IBMPlexSansCondensed-OFL.txt +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/fonts/__init__.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/intents.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/layout.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/linting/__init__.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/linting/coverage.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/linting/issues.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/linting/structural.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/make_drawing.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/model/declare.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/model/planner.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/pmi.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/projection.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/recognition/__init__.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/recognition/_features.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/recognition/levels.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/recognition/slots.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/recognition/turned.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/registry.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/src/draftwright/repair.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/_kernel.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/fixtures/nist_ctc_01_asme1_ap203.stp +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/fixtures/nist_ctc_01_asme1_ap242.stp +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/fixtures/nist_ctc_02_asme1_ap203.stp +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/fixtures/nist_ctc_02_asme1_ap242.stp +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/fixtures/nist_ctc_03_asme1_ap203.stp +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/fixtures/nist_ctc_03_asme1_ap242.stp +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/fixtures/nist_ctc_04_asme1_ap203.stp +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/fixtures/nist_ctc_04_asme1_ap242.stp +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/fixtures/nist_ctc_05_asme1_ap203.stp +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/fixtures/nist_ctc_05_asme1_ap242.stp +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/layout_snapshots/box.json +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/layout_snapshots/bracket.json +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/layout_snapshots/drive_screw_x.json +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/layout_snapshots/flange.json +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/layout_snapshots/holed_slot.json +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/layout_snapshots/plate_holes.json +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/layout_snapshots/slotted.json +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/layout_snapshots/turned_shaft.json +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_declare.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_e2e_slice.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_e2e_standards.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_fits.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_gdt_placement.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_layout.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_layout_cleanliness.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_layout_property.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_layout_snapshot.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_lint_reconciliation.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_lint_structural.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_linting.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_object_aspects.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_part_model.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_recognition.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_registry.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_sheet_gdt.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_sheet_notes.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_sheet_of.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_sheet_tables.py +0 -0
- {draftwright-0.2.11 → draftwright-0.2.13}/tests/test_turned_steps.py +0 -0
|
@@ -1,5 +1,75 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.2.13 — 2026-07-09
|
|
4
|
+
|
|
5
|
+
**Compose-then-pack is now the layout authority.** This patch finishes the
|
|
6
|
+
ViewBlock footprint migration: estimated and measured layout paths now use the
|
|
7
|
+
same composed block model, furniture declarations reserve their real footprint,
|
|
8
|
+
and measure/repack iterates until stable instead of trusting one measured pass.
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **ViewBlock footprints now drive the orthographic stack.** The old
|
|
13
|
+
estimator-only plan-view balloon lift is gone; plan balloon headroom is part
|
|
14
|
+
of the plan block in both estimated and measured/repack layout. (#112)
|
|
15
|
+
- **Measure/repack iterates to a fixed point.** Repack now keeps measuring and
|
|
16
|
+
repacking while view-owned footprints continue to grow, with bounded
|
|
17
|
+
convergence and tests for multi-pass growth. (#302)
|
|
18
|
+
- **Estimated view-block composition is explicit.** Scale/page fitness, repack,
|
|
19
|
+
and furniture placement share the same composed block semantics instead of
|
|
20
|
+
rebuilding corridor estimates independently.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- **Declared callout furniture reserves layout footprint.** User-declared
|
|
25
|
+
furniture is now measured and fed into the layout model, reducing post-hoc
|
|
26
|
+
collisions from declarations that previously existed only at render time.
|
|
27
|
+
- **Annotation-box composition owns strip sizing.** Strip depths reduce from
|
|
28
|
+
composed annotation boxes, making footprint sizing less dependent on divergent
|
|
29
|
+
scalar estimates.
|
|
30
|
+
|
|
31
|
+
### Documentation
|
|
32
|
+
|
|
33
|
+
- **Layout architecture docs reflect the current state.** ADR 0004 and the
|
|
34
|
+
layout primer now mark the core layout-authority tranche complete and separate
|
|
35
|
+
remaining work into hardening, coverage, detail-view, and manual-intent phases.
|
|
36
|
+
|
|
37
|
+
## v0.2.12 — 2026-07-09
|
|
38
|
+
|
|
39
|
+
**Sheet scripts now round-trip authored layout intent.** This patch finishes the next
|
|
40
|
+
slice of the layout-authority work: below/right ladders, pinned edits, height ladders,
|
|
41
|
+
detected envelopes, and AP242 authored dimensions now survive the declarative Sheet path
|
|
42
|
+
without being invalidated by post-hoc placement or raw imported metadata.
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
|
|
46
|
+
- **Pinned edit intents route through the corridor solve.** `locate(..., pin=True)` and
|
|
47
|
+
pinned dimensions now become priority-ranked candidates in the shared corridor instead
|
|
48
|
+
of fixed post-hoc edits, so user intent participates in the same ordering/spacing model
|
|
49
|
+
as automatic dimensions. (#511)
|
|
50
|
+
- **AP242 dimensional PMI lowers to authored drafting dimensions.** Imported AP242
|
|
51
|
+
size/location dimensions now become `AuthoredDimension` IR and generated Sheet scripts
|
|
52
|
+
emit `sheet.dimension(...)`; unsupported PMI records remain explicit raw fallbacks.
|
|
53
|
+
(#503, #422, #62)
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- **`place_dim` is deprecated.** Manual dimensions should use pinned candidate/dimension
|
|
58
|
+
intent instead of the old incremental edit path.
|
|
59
|
+
- **Prismatic height ladders and detected envelopes round-trip through Sheet scripts.**
|
|
60
|
+
Generated scripts now preserve `StepLevelFeature` and detected `EnvelopeFeature`
|
|
61
|
+
fallbacks, preventing CTC01-style ladder swaps and raw STEP-envelope remeasurement
|
|
62
|
+
drift.
|
|
63
|
+
|
|
64
|
+
### Fixed
|
|
65
|
+
|
|
66
|
+
- **Below/right corridors now share one solve.** Side-hole locations, height ladders,
|
|
67
|
+
PMI/GD&T, envelope dims, and related below/right strip occupants negotiate in the shared
|
|
68
|
+
corridor rather than competing through separate late passes. (#477)
|
|
69
|
+
- **Sheet script parity is tighter.** Generated scripts preserve member positions, step
|
|
70
|
+
levels, AP242 authored dimensions, raw PMI fallbacks, and detected envelopes so
|
|
71
|
+
direct-vs-script CTC01 output has matching annotation names.
|
|
72
|
+
|
|
3
73
|
## v0.2.11 — 2026-07-08
|
|
4
74
|
|
|
5
75
|
**Automatic layout now has one authority.** Page/scale selection, section placement,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: draftwright
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.13
|
|
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
|
|
@@ -842,10 +842,9 @@ automatically, so you say *what* to dimension, not *where*:
|
|
|
842
842
|
```python
|
|
843
843
|
dwg = build_drawing(part)
|
|
844
844
|
|
|
845
|
-
# Inspect detected features and add a dimension in domain terms
|
|
846
|
-
for f in dwg.features
|
|
847
|
-
|
|
848
|
-
"below", "plan", dwg.draft, name="dim_pocket")
|
|
845
|
+
# Inspect detected features and add a pinned dimension in domain terms:
|
|
846
|
+
env = next(f for f in dwg.model().features if f.kind == "envelope")
|
|
847
|
+
dwg.dimension(env, "length", role="width", side="below", pin=True)
|
|
849
848
|
|
|
850
849
|
crit = dwg.lint_summary() # {"passed", "score", "by_code", "issues":[…suggestion]}
|
|
851
850
|
dwg.repair() # auto-fix mechanically-fixable lint; never worsens
|
|
@@ -876,6 +875,7 @@ the section A–A trigger, the prismatic step-ladder + rotational furniture, and
|
|
|
876
875
|
are all on the IR — the migration is complete (one path; the orchestrator is
|
|
877
876
|
build → plan → render). See
|
|
878
877
|
[`docs/target-architecture.md`](docs/target-architecture.md) and
|
|
878
|
+
[`docs/layout-algorithm-primer.md`](docs/layout-algorithm-primer.md) for a short walkthrough, plus
|
|
879
879
|
[`docs/adr/`](docs/adr/). The engine handles view layout (strip/zone model), scale
|
|
880
880
|
selection, annotation placement, and section rendering.
|
|
881
881
|
|
|
@@ -149,10 +149,9 @@ automatically, so you say *what* to dimension, not *where*:
|
|
|
149
149
|
```python
|
|
150
150
|
dwg = build_drawing(part)
|
|
151
151
|
|
|
152
|
-
# Inspect detected features and add a dimension in domain terms
|
|
153
|
-
for f in dwg.features
|
|
154
|
-
|
|
155
|
-
"below", "plan", dwg.draft, name="dim_pocket")
|
|
152
|
+
# Inspect detected features and add a pinned dimension in domain terms:
|
|
153
|
+
env = next(f for f in dwg.model().features if f.kind == "envelope")
|
|
154
|
+
dwg.dimension(env, "length", role="width", side="below", pin=True)
|
|
156
155
|
|
|
157
156
|
crit = dwg.lint_summary() # {"passed", "score", "by_code", "issues":[…suggestion]}
|
|
158
157
|
dwg.repair() # auto-fix mechanically-fixable lint; never worsens
|
|
@@ -183,6 +182,7 @@ the section A–A trigger, the prismatic step-ladder + rotational furniture, and
|
|
|
183
182
|
are all on the IR — the migration is complete (one path; the orchestrator is
|
|
184
183
|
build → plan → render). See
|
|
185
184
|
[`docs/target-architecture.md`](docs/target-architecture.md) and
|
|
185
|
+
[`docs/layout-algorithm-primer.md`](docs/layout-algorithm-primer.md) for a short walkthrough, plus
|
|
186
186
|
[`docs/adr/`](docs/adr/). The engine handles view layout (strip/zone model), scale
|
|
187
187
|
selection, annotation placement, and section rendering.
|
|
188
188
|
|
|
@@ -14,6 +14,7 @@ import logging
|
|
|
14
14
|
import math
|
|
15
15
|
import warnings
|
|
16
16
|
from collections.abc import Callable
|
|
17
|
+
from dataclasses import replace
|
|
17
18
|
|
|
18
19
|
from build123d import Compound, Shape
|
|
19
20
|
from build123d_drafting.helpers import draft_preset
|
|
@@ -31,6 +32,8 @@ from draftwright._core import (
|
|
|
31
32
|
_legible_steps,
|
|
32
33
|
_Projector,
|
|
33
34
|
)
|
|
35
|
+
from draftwright.model.ir import Datum, PartModel, StepFeature
|
|
36
|
+
from draftwright.model.planner import plan_dimensions
|
|
34
37
|
from draftwright.recognition import (
|
|
35
38
|
analyse_cylinders,
|
|
36
39
|
analyse_face_levels,
|
|
@@ -45,6 +48,7 @@ from draftwright.sheet import (
|
|
|
45
48
|
StripDepths,
|
|
46
49
|
_build_zones,
|
|
47
50
|
_est_hole_table_sizes,
|
|
51
|
+
_est_planned_bore_callout_width,
|
|
48
52
|
_layout_geometry,
|
|
49
53
|
_measure_strips,
|
|
50
54
|
_will_section,
|
|
@@ -96,6 +100,32 @@ def _declared_will_section(model, *, is_rotational=False, cx=0.0, cy=0.0) -> boo
|
|
|
96
100
|
return False
|
|
97
101
|
|
|
98
102
|
|
|
103
|
+
def _coerce_layout_model(model, part, decorations=None) -> PartModel | None:
|
|
104
|
+
"""Return the caller-declared IR with authored decorations for layout sizing.
|
|
105
|
+
|
|
106
|
+
This mirrors the builder's render-time coercion, but stays local to analysis so
|
|
107
|
+
page/scale/strip selection can see the same authored callout text the renderer
|
|
108
|
+
will later place (#450).
|
|
109
|
+
"""
|
|
110
|
+
if model is None:
|
|
111
|
+
return None
|
|
112
|
+
if isinstance(model, PartModel):
|
|
113
|
+
if decorations:
|
|
114
|
+
return replace(model, decorations={**model.decorations, **decorations})
|
|
115
|
+
return model
|
|
116
|
+
features = list(model)
|
|
117
|
+
bbox = part.bounding_box()
|
|
118
|
+
orientation = next((f.frame.axis for f in features if isinstance(f, StepFeature)), None)
|
|
119
|
+
datum = Datum(id="datum_xy", kind="point", at=(bbox.min.X, bbox.min.Y, bbox.min.Z))
|
|
120
|
+
return PartModel(
|
|
121
|
+
bbox=bbox,
|
|
122
|
+
orientation=orientation,
|
|
123
|
+
features=features,
|
|
124
|
+
datums=[datum],
|
|
125
|
+
decorations=decorations or {},
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
99
129
|
def _import_step(path) -> Compound:
|
|
100
130
|
"""Read solid geometry from a STEP file via OCCT's ``STEPControl_Reader``.
|
|
101
131
|
|
|
@@ -299,6 +329,7 @@ def _analyse(
|
|
|
299
329
|
page=None,
|
|
300
330
|
pmi="off",
|
|
301
331
|
model=None,
|
|
332
|
+
decorations=None,
|
|
302
333
|
) -> Analysis:
|
|
303
334
|
"""Load STEP or use a build123d Shape, analyse geometry, compute layout.
|
|
304
335
|
|
|
@@ -409,6 +440,17 @@ def _analyse(
|
|
|
409
440
|
_draft_est = draft_preset(font_size=_FONT_SIZE, decimal_precision=1)
|
|
410
441
|
_arrow_length = _draft_est.arrow_length
|
|
411
442
|
_pad_around_text = _draft_est.pad_around_text
|
|
443
|
+
layout_model = _coerce_layout_model(model, part, decorations)
|
|
444
|
+
declared_bore_width = (
|
|
445
|
+
_est_planned_bore_callout_width(
|
|
446
|
+
plan_dimensions(layout_model),
|
|
447
|
+
_draft_est,
|
|
448
|
+
font_size=_FONT_SIZE,
|
|
449
|
+
pad_around_text=_pad_around_text,
|
|
450
|
+
)
|
|
451
|
+
if layout_model is not None
|
|
452
|
+
else 0.0
|
|
453
|
+
)
|
|
412
454
|
holes = find_holes(part, cyls=(z_cyls, cross_cyls))
|
|
413
455
|
patterns = find_hole_patterns(holes)
|
|
414
456
|
bosses = find_bosses(part, cyls=(z_cyls, cross_cyls)) # detect once — the one inventory (#264)
|
|
@@ -442,6 +484,7 @@ def _analyse(
|
|
|
442
484
|
bb,
|
|
443
485
|
arrow_length=_arrow_length,
|
|
444
486
|
pad_around_text=_pad_around_text,
|
|
487
|
+
declared_bore_callout_width=declared_bore_width,
|
|
445
488
|
)
|
|
446
489
|
|
|
447
490
|
def _pick_for_step_count(n_steps_i: int, strips_i: StripDepths) -> _ScalePick:
|
|
@@ -518,6 +561,7 @@ def _analyse(
|
|
|
518
561
|
bb,
|
|
519
562
|
arrow_length=_arrow_length,
|
|
520
563
|
pad_around_text=_pad_around_text,
|
|
564
|
+
declared_bore_callout_width=declared_bore_width,
|
|
521
565
|
)
|
|
522
566
|
# View positions + iso empty-rectangle, shared with scale selection (_fits)
|
|
523
567
|
# via _layout_geometry so placement and fit never diverge (#11). _fit_iso_view
|
|
@@ -293,6 +293,10 @@ class CorridorCandidate:
|
|
|
293
293
|
``priority`` is kept. An authored GD&T frame sets this above the auto dims so it
|
|
294
294
|
is not dropped in favour of a lower-value auto dim purely by stacking-key order.
|
|
295
295
|
Default 0 (every auto dim) → key order, unchanged.
|
|
296
|
+
anchored/natural: when ``anchored`` is true, the strip solve keeps this candidate
|
|
297
|
+
near its own natural stacking-axis page coordinate instead of the segment edge.
|
|
298
|
+
This is how user-authored pinned dimension intents join the shared solve
|
|
299
|
+
without being invalidated by a later first-fit pass.
|
|
296
300
|
on_place/on_drop: the pass's own post-placement bookkeeping — coverage
|
|
297
301
|
registration / drop lint + `Escalation`, or a slot's below-side fallthrough.
|
|
298
302
|
force: policy-B force-keep after the corridor-respecting pass (locations have
|
|
@@ -307,6 +311,8 @@ class CorridorCandidate:
|
|
|
307
311
|
dedup: tuple | None = None
|
|
308
312
|
precedence: int = 0
|
|
309
313
|
priority: float = 0
|
|
314
|
+
anchored: bool = False
|
|
315
|
+
natural: float | None = None
|
|
310
316
|
force: bool = False
|
|
311
317
|
# The source IR feature this dim was rendered for — recorded as provenance when the
|
|
312
318
|
# dim is placed at drain (ADR 0010). ``None`` leaves the annotation feature-less.
|
|
@@ -376,6 +382,8 @@ def solve_corridor(dwg, strip, view, axis, cands, tier):
|
|
|
376
382
|
sizes = {c.name: c.size for c in kept if c.size is not None} # real footprint (#61)
|
|
377
383
|
forbid = {c.name: c.forbid for c in kept if c.forbid is not None} # title-block box (#481)
|
|
378
384
|
prio = {c.name: c.priority for c in kept if c.priority} # over-capacity survival rank (#357)
|
|
385
|
+
anchored = {c.name: c.anchored for c in kept if c.anchored}
|
|
386
|
+
naturals = {c.name: c.natural for c in kept if c.natural is not None}
|
|
379
387
|
left = {
|
|
380
388
|
n
|
|
381
389
|
for n, _ in place_strip_candidates(
|
|
@@ -389,6 +397,8 @@ def solve_corridor(dwg, strip, view, axis, cands, tier):
|
|
|
389
397
|
sizes=sizes,
|
|
390
398
|
forbid=forbid,
|
|
391
399
|
priorities=prio,
|
|
400
|
+
anchored=anchored,
|
|
401
|
+
naturals=naturals,
|
|
392
402
|
)
|
|
393
403
|
}
|
|
394
404
|
force_pairs = [(c.name, c.build) for c in kept if c.name in left and c.force]
|
|
@@ -407,6 +417,8 @@ def solve_corridor(dwg, strip, view, axis, cands, tier):
|
|
|
407
417
|
sizes=sizes,
|
|
408
418
|
forbid=forbid,
|
|
409
419
|
priorities=prio,
|
|
420
|
+
anchored=anchored,
|
|
421
|
+
naturals=naturals,
|
|
410
422
|
)
|
|
411
423
|
}
|
|
412
424
|
if force_pairs
|
|
@@ -425,10 +437,13 @@ def solve_corridor(dwg, strip, view, axis, cands, tier):
|
|
|
425
437
|
def register_corridor(dwg, key, strip, view, axis, tier, cand):
|
|
426
438
|
"""Queue a :class:`CorridorCandidate` under a shared corridor *key* so one
|
|
427
439
|
:func:`drain_corridors` places the whole cross-pass set together (ADR 0009 end state).
|
|
428
|
-
The first registration for a key fixes its ``(strip, view, axis
|
|
440
|
+
The first registration for a key fixes its ``(strip, view, axis)``; mixed producers on
|
|
441
|
+
the same corridor use the largest requested tier so spacing is not registration-order
|
|
442
|
+
dependent."""
|
|
429
443
|
b = dwg._corridor_batch.setdefault(
|
|
430
444
|
key, {"strip": strip, "view": view, "axis": axis, "tier": tier, "cands": []}
|
|
431
445
|
)
|
|
446
|
+
b["tier"] = max(b["tier"], tier)
|
|
432
447
|
b["cands"].append(cand)
|
|
433
448
|
|
|
434
449
|
|
|
@@ -453,6 +468,8 @@ def place_strip_candidates(
|
|
|
453
468
|
sizes=None,
|
|
454
469
|
forbid=None,
|
|
455
470
|
priorities=None,
|
|
471
|
+
anchored=None,
|
|
472
|
+
naturals=None,
|
|
456
473
|
):
|
|
457
474
|
"""Collect-then-solve placement of location/feature dims on one strip (ADR 0009).
|
|
458
475
|
The single shared strip placer that retires the ``Strip.allocate`` cursor (#150,
|
|
@@ -483,6 +500,11 @@ def place_strip_candidates(
|
|
|
483
500
|
the lowest ``(priority, key)``, so a higher priority is kept — an authored GD&T frame
|
|
484
501
|
is not dropped for a lower-value auto dim purely by stacking-key order.
|
|
485
502
|
|
|
503
|
+
*anchored* and *naturals* opt individual candidates into the weighted anchoring
|
|
504
|
+
mode in :func:`plan_strip`. This preserves the old segment-edge natural for every
|
|
505
|
+
caller that does not pass them, while letting authored pinned candidates express the
|
|
506
|
+
page coordinate they asked for inside the same shared solve.
|
|
507
|
+
|
|
486
508
|
``force=True`` skips that corridor check — the caller's last resort when no view took
|
|
487
509
|
the dim cleanly: keep it on its natural view and accept the (same-feature) leader
|
|
488
510
|
crossing rather than drop a real dimension (policy B). Candidates that find no strip
|
|
@@ -562,7 +584,6 @@ def place_strip_candidates(
|
|
|
562
584
|
|
|
563
585
|
def _evaluate_segment(take, seg_lo, seg_hi):
|
|
564
586
|
nat = seg_lo if inner == lo else seg_hi
|
|
565
|
-
anch = (0.0, nat) if axis == "y" else (nat, 0.0)
|
|
566
587
|
# Keys order the tiers so the FIRST candidate lands on the inner tier: for an
|
|
567
588
|
# inner=lo strip that is the lowest position (ascending keys); for a below strip
|
|
568
589
|
# (inner=hi) it is the highest, so the keys reverse.
|
|
@@ -570,9 +591,14 @@ def place_strip_candidates(
|
|
|
570
591
|
(
|
|
571
592
|
StripCandidate(
|
|
572
593
|
f"{(k if inner == lo else len(take) - 1 - k):04d}",
|
|
573
|
-
|
|
594
|
+
(
|
|
595
|
+
(0.0, (naturals or {}).get(nb[0], nat))
|
|
596
|
+
if axis == "y"
|
|
597
|
+
else ((naturals or {}).get(nb[0], nat), 0.0)
|
|
598
|
+
),
|
|
574
599
|
(sizes or {}).get(nb[0], (tier, tier)),
|
|
575
600
|
priority=(priorities or {}).get(nb[0], 0.0),
|
|
601
|
+
anchored=(anchored or {}).get(nb[0], False),
|
|
576
602
|
),
|
|
577
603
|
nb,
|
|
578
604
|
)
|