draftwright 0.3.4__tar.gz → 0.3.5__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.4 → draftwright-0.3.5}/CHANGELOG.md +39 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/PKG-INFO +2 -2
- {draftwright-0.3.4 → draftwright-0.3.5}/pyproject.toml +2 -2
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/_core.py +144 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/analysis.py +19 -1
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/annotations/orchestrator.py +25 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/builder.py +22 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/cli.py +18 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/compose.py +23 -2
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/linting/structural.py +16 -1
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/sheet.py +6 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/sheet_emit.py +15 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_make_drawing.py +142 -1
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_sheet_emit.py +43 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/.gitignore +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/LICENSE +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/README.md +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/docs/adr/README.md +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/skills/SKILL.md +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/__init__.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/_geometry.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/annotate.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/annotations/__init__.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/annotations/_common.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/annotations/balloons.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/annotations/from_model.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/annotations/holes.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/annotations/sections.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/drawing.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/export.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/fits.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/fonts/IBMPlexMono-Regular.ttf +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/fonts/IBMPlexSansCondensed-Regular.ttf +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/fonts/LICENSE-IBMPlexMono-OFL.txt +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/fonts/LICENSE-IBMPlexSansCondensed-OFL.txt +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/fonts/__init__.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/intents.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/layout.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/linting/__init__.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/linting/coverage.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/linting/issues.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/linting/suggest.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/make_drawing.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/model/__init__.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/model/declare.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/model/detect.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/model/ir.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/model/planner.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/pmi.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/projection.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/recognition/__init__.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/recognition/_features.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/recognition/_record.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/recognition/chamfers.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/recognition/countersinks.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/recognition/fillets.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/recognition/flats.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/recognition/grooves.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/recognition/levels.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/recognition/plates.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/recognition/slots.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/recognition/turned.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/registry.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/repair.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/score.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/src/draftwright/sheet_dsl.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/_kernel.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/_layout_sig.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/fixtures/grm03_thumbwheel_drive_screw.step +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/fixtures/nist_ctc_01_asme1_ap203.stp +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/fixtures/nist_ctc_01_asme1_ap242.stp +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/fixtures/nist_ctc_02_asme1_ap203.stp +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/fixtures/nist_ctc_02_asme1_ap242.stp +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/fixtures/nist_ctc_03_asme1_ap203.stp +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/fixtures/nist_ctc_03_asme1_ap242.stp +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/fixtures/nist_ctc_04_asme1_ap203.stp +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/fixtures/nist_ctc_04_asme1_ap242.stp +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/fixtures/nist_ctc_05_asme1_ap203.stp +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/fixtures/nist_ctc_05_asme1_ap242.stp +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/refactor_golden/bracket_section.json +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/refactor_golden/centered_rebate.json +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/refactor_golden/chamfered.json +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/refactor_golden/filleted.json +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/refactor_golden/flange_dense.json +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/refactor_golden/grid_plate.json +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/refactor_golden/grooved_shaft.json +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/refactor_golden/hex_bar.json +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/refactor_golden/holed_slot.json +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/refactor_golden/pocketed.json +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/refactor_golden/prismatic_ladder.json +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/refactor_golden/scattered_plate.json +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/refactor_golden/side_drilled.json +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/refactor_golden/turned_stepped.json +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_architecture_docs.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_carve_free_position_callers.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_declare.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_detect_once.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_drawing_encapsulation.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_e2e_slice.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_e2e_standards.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_export_dxf_zoom.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_fillets_adjacency.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_fits.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_gdt_placement.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_import_boundaries.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_layout.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_layout_cleanliness.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_layout_hypothesis.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_layout_property.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_lint_box_cache.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_lint_reconciliation.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_lint_structural.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_linting.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_object_aspects.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_occupancy_boxes.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_part_model.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_pitch_dim_footprint.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_pmi.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_private_test_imports.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_recogniser_contract.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_recognition.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_refactor_golden.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_registry.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_render_seam.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_score.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_script_detail_parity.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_sheet_gdt.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_sheet_notes.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_sheet_of.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_sheet_tables.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_solve_trace.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_strip_layout.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_tolerances.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_turned_steps.py +0 -0
- {draftwright-0.3.4 → draftwright-0.3.5}/tests/test_witness_label_reconciliation.py +0 -0
|
@@ -2,6 +2,45 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## v0.3.5 — 2026-07-20
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **Generated `Sheet` script round-trips `--frame` / `--zones` / `--projection`**:
|
|
10
|
+
the sheet-furniture flags added in #767/#768/#769 were not emitted into the
|
|
11
|
+
generated `Sheet(...)` constructor (nor forwarded by the CLI's `--script` path),
|
|
12
|
+
so a regenerated script dropped the border / zone ruler / projection symbol —
|
|
13
|
+
the script no longer matched the direct CLI drawing. The emitter now emits
|
|
14
|
+
`frame=True` / `zones=True` / `projection=…` when set (a plain drawing keeps a
|
|
15
|
+
clean constructor), and the CLI forwards them to `generate_sheet_script`.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **ISO 5457 zone-grid border ruler** (#768): `build_drawing(zones=True)` /
|
|
20
|
+
`Sheet(zones=True)` / `--zones` draws the grid reference system — numbers 1..
|
|
21
|
+
along the top/bottom edges, letters A.. (skipping I/O) down the sides — in the
|
|
22
|
+
band between the frame and the page edge, with the standard division count per
|
|
23
|
+
A-series page (~50 mm zones). Implies a frame (the ticks sit on it). Off by
|
|
24
|
+
default → byte-identical. The border ticks are lint-exempt like the frame; the
|
|
25
|
+
labels are exempt only from the page-bounds check (they legitimately sit outside
|
|
26
|
+
the drawable), still covered by overlap lint.
|
|
27
|
+
- **Projection-method symbol** (#769): `build_drawing(projection="third"|"first")` /
|
|
28
|
+
`Sheet(projection=…)` / `--projection` draws the ISO 5456-2 third-/first-angle
|
|
29
|
+
glyph in the reserved title-block band (above the title block). Uses the new
|
|
30
|
+
`ProjectionSymbol` primitive from build123d-drafting-helpers **0.14.1** (the pin
|
|
31
|
+
is bumped). Off by default → byte-identical. Unlike the page-spanning frame, the
|
|
32
|
+
small well-placed glyph is *not* lint-exempt — lint covers it so a future
|
|
33
|
+
mispositioning is caught.
|
|
34
|
+
- **Opt-in drawn sheet frame / border** (#767): `build_drawing(frame=True)` /
|
|
35
|
+
`Sheet(frame=True)` / `--frame` draws a border rectangle at the page margin. It's
|
|
36
|
+
the *content boundary* (Option B), not a rectangle over the drawing: turning it on
|
|
37
|
+
raises the effective content margin, and because `compose._layout_geometry` is the
|
|
38
|
+
single authority shared by scale/page selection **and** placement, the reservation
|
|
39
|
+
flows through `choose_scale` — so a framed drawing may pick a smaller scale / larger
|
|
40
|
+
page (ADR 0004), with content clearing the border. Default `frame=False` is
|
|
41
|
+
byte-identical (guarded by the golden + layout-cleanliness suites). The page-spanning
|
|
42
|
+
border carries an `is_sheet_frame` rider so lint skips it.
|
|
43
|
+
|
|
5
44
|
## v0.3.4 — 2026-07-20
|
|
6
45
|
|
|
7
46
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: draftwright
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.5
|
|
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
|
|
@@ -682,7 +682,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
682
682
|
Classifier: Programming Language :: Python :: 3.14
|
|
683
683
|
Classifier: Topic :: Scientific/Engineering
|
|
684
684
|
Requires-Python: <3.15,>=3.10
|
|
685
|
-
Requires-Dist: build123d-drafting-helpers>=0.14.
|
|
685
|
+
Requires-Dist: build123d-drafting-helpers>=0.14.1
|
|
686
686
|
Requires-Dist: build123d<0.11,>=0.9.0; python_full_version < '3.13'
|
|
687
687
|
Requires-Dist: build123d<0.12,>=0.11; python_full_version >= '3.13'
|
|
688
688
|
Requires-Dist: numpy>=1.24
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "draftwright"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.5"
|
|
8
8
|
description = "Automated technical-drawing generation for build123d"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -17,7 +17,7 @@ dependencies = [
|
|
|
17
17
|
# 3.13+. Mirrors pzfreo/build123d-mcp's dependency handling.
|
|
18
18
|
"build123d>=0.9.0,<0.11 ; python_full_version < '3.13'",
|
|
19
19
|
"build123d>=0.11,<0.12 ; python_full_version >= '3.13'",
|
|
20
|
-
"build123d-drafting-helpers>=0.14.
|
|
20
|
+
"build123d-drafting-helpers>=0.14.1",
|
|
21
21
|
"numpy>=1.24",
|
|
22
22
|
# PNG raster export (SVG→PDF→PNG). Both permissively licensed (Pillow HPND; pypdfium2
|
|
23
23
|
# Apache-2.0 wrapping Google PDFium BSD-3) and ship pre-built wheels with NO native system
|
|
@@ -50,6 +50,44 @@ _log = logging.getLogger(__name__)
|
|
|
50
50
|
|
|
51
51
|
_MARGIN = 10.0
|
|
52
52
|
|
|
53
|
+
# When a sheet frame is drawn (#767), content reserves this extra band inside the border so
|
|
54
|
+
# it clears the drawn line rather than sitting on it. The frame draws AT _MARGIN (the old
|
|
55
|
+
# drawable boundary); content insets to _content_margin(frame).
|
|
56
|
+
_FRAME_BAND = 6.0
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _content_margin(frame: bool) -> float:
|
|
60
|
+
"""The effective content margin: ``_MARGIN``, plus the frame clearance band when a sheet
|
|
61
|
+
frame is drawn (#767). Threaded into the layout authority so the reservation flows through
|
|
62
|
+
scale/page selection (ADR 0004), not just the render."""
|
|
63
|
+
return _MARGIN + (_FRAME_BAND if frame else 0.0)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# ISO 5457 zone-grid letters (vertical edges): A.. skipping I and O (confusable with 1 / 0).
|
|
67
|
+
_ZONE_LETTERS = "ABCDEFGHJKLMNPQRSTUVWXYZ"
|
|
68
|
+
|
|
69
|
+
# ISO 5457 reference-grid division counts (columns=numbers × rows=letters) per A-series page,
|
|
70
|
+
# keyed by the full (width, height) so a same-width custom page doesn't borrow the count.
|
|
71
|
+
_ZONE_DIVISIONS = {
|
|
72
|
+
(297, 210): (6, 4),
|
|
73
|
+
(420, 297): (8, 6),
|
|
74
|
+
(594, 420): (12, 8),
|
|
75
|
+
(841, 594): (16, 12),
|
|
76
|
+
(1189, 841): (24, 16),
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _zone_divisions(page_w: float, page_h: float) -> tuple[int, int]:
|
|
81
|
+
"""``(columns, rows)`` for the ISO 5457 zone grid (#768) — the standard count for an
|
|
82
|
+
A-series page (matched on BOTH dimensions), else ~50 mm zones for a custom page. Rows are
|
|
83
|
+
clamped to the available letters (I/O skipped) so a very tall custom page can't over-index."""
|
|
84
|
+
std = _ZONE_DIVISIONS.get((int(round(page_w)), int(round(page_h))))
|
|
85
|
+
if std is not None:
|
|
86
|
+
return std
|
|
87
|
+
cols = max(2, round(page_w / 49.5))
|
|
88
|
+
rows = min(len(_ZONE_LETTERS), max(2, round(page_h / 49.5)))
|
|
89
|
+
return (cols, rows)
|
|
90
|
+
|
|
53
91
|
|
|
54
92
|
_TB_CLEAR = _MARGIN + 1.0 # title-block inset: one extra mm over _MARGIN for clearance
|
|
55
93
|
|
|
@@ -711,6 +749,13 @@ class Analysis:
|
|
|
711
749
|
date: str = ""
|
|
712
750
|
revision: str = "A"
|
|
713
751
|
company: str = ""
|
|
752
|
+
# Draw a sheet border/frame (#767). When True, `margin` is already the reserved content
|
|
753
|
+
# margin (`_content_margin(True)`), so content clears the frame drawn at `_MARGIN`.
|
|
754
|
+
frame: bool = False
|
|
755
|
+
# Projection-method symbol (#769): "third" / "first" (ISO 5456-2) or None (omit).
|
|
756
|
+
projection: str | None = None
|
|
757
|
+
# Draw the ISO 5457 zone-grid border ruler (#768). Implies a frame (the ticks sit on it).
|
|
758
|
+
zones: bool = False
|
|
714
759
|
# The PartModel built by _analyse's pre-scale sizing pass (#584 WP1 A) — stored so
|
|
715
760
|
# the render path reuses it instead of re-running the detectors (ADR 0008 Amdt 5:
|
|
716
761
|
# one inventory, detected once; #602). Typed `object` to keep _core free of a
|
|
@@ -799,6 +844,105 @@ def _add_title_block(dwg, a: Analysis):
|
|
|
799
844
|
dwg.add(tb, "title_block")
|
|
800
845
|
|
|
801
846
|
|
|
847
|
+
def _make_sheet_frame(a: Analysis) -> Compound:
|
|
848
|
+
"""The sheet border rectangle (#767) — a closed outline at the ``_MARGIN`` inset (the old
|
|
849
|
+
drawable boundary). Content clears it because ``a.margin`` is the reserved content margin.
|
|
850
|
+
Carries an ``is_sheet_frame`` rider (like ``is_centerline``) so lint skips its page-spanning
|
|
851
|
+
box, and so ``get_annotation`` / a removed frame drop it cleanly."""
|
|
852
|
+
x0, y0 = _MARGIN, _MARGIN
|
|
853
|
+
x1, y1 = a.PAGE_W - _MARGIN, a.PAGE_H - _MARGIN
|
|
854
|
+
frame = Compound(
|
|
855
|
+
children=[
|
|
856
|
+
Edge.make_line(Vector(x0, y0, 0), Vector(x1, y0, 0)),
|
|
857
|
+
Edge.make_line(Vector(x1, y0, 0), Vector(x1, y1, 0)),
|
|
858
|
+
Edge.make_line(Vector(x1, y1, 0), Vector(x0, y1, 0)),
|
|
859
|
+
Edge.make_line(Vector(x0, y1, 0), Vector(x0, y0, 0)),
|
|
860
|
+
]
|
|
861
|
+
)
|
|
862
|
+
frame.is_sheet_frame = True # furniture, not a dimension/view — exempt from overlap lint
|
|
863
|
+
return frame
|
|
864
|
+
|
|
865
|
+
|
|
866
|
+
def _add_sheet_frame(dwg, a: Analysis):
|
|
867
|
+
"""Add the sheet border (#767), drawn last like the title block. No-op is the caller's
|
|
868
|
+
(gated on ``a.frame``)."""
|
|
869
|
+
dwg.add(_make_sheet_frame(a), "sheet_frame")
|
|
870
|
+
|
|
871
|
+
|
|
872
|
+
def _add_projection_symbol(dwg, a: Analysis):
|
|
873
|
+
"""Place the ISO 5456-2 projection-method glyph (#769) in the reserved title-block band,
|
|
874
|
+
just above the drawn title block (deterministic empty space — the block reserves _TB_H but
|
|
875
|
+
draws shorter). Registered ``projection_symbol`` with an ``is_projection_symbol`` identity
|
|
876
|
+
rider. Unlike the page-spanning frame it is NOT lint-exempt: it's a small, well-placed glyph,
|
|
877
|
+
so lint covers it and a future mispositioning is caught. Gated on ``a.projection``."""
|
|
878
|
+
from build123d_drafting import ProjectionSymbol
|
|
879
|
+
|
|
880
|
+
sym = ProjectionSymbol(
|
|
881
|
+
a.projection,
|
|
882
|
+
draft=draft_preset(
|
|
883
|
+
font_size=dwg.draft.font_size,
|
|
884
|
+
decimal_precision=dwg.draft.decimal_precision,
|
|
885
|
+
font_path=PLEX_SANS_CONDENSED,
|
|
886
|
+
),
|
|
887
|
+
)
|
|
888
|
+
b = sym.bounding_box()
|
|
889
|
+
bx, by = (b.min.X + b.max.X) / 2, (b.min.Y + b.max.Y) / 2
|
|
890
|
+
w, h = b.max.X - b.min.X, b.max.Y - b.min.Y
|
|
891
|
+
# Right side of the title-block column, near the top of its reserved band.
|
|
892
|
+
cx = a.PAGE_W - _TB_CLEAR - w / 2 - 3
|
|
893
|
+
cy = _TB_CLEAR + _TB_H - h / 2 - 2
|
|
894
|
+
sym = sym.locate(Location((cx - bx, cy - by, 0)))
|
|
895
|
+
sym.is_projection_symbol = True
|
|
896
|
+
dwg.add(sym, "projection_symbol")
|
|
897
|
+
|
|
898
|
+
|
|
899
|
+
def _add_zone_grid(dwg, a: Analysis):
|
|
900
|
+
"""Draw the ISO 5457 zone-grid border ruler (#768) — numbers 1.. along the top/bottom
|
|
901
|
+
edges, letters A.. (skipping I/O) down the left/right — in the band between the frame
|
|
902
|
+
(at ``_MARGIN``) and the page edge. Requires ``a.frame`` (the ticks sit on the border);
|
|
903
|
+
the caller gates on ``a.zones`` and ensures the frame. Tick lines register as ``zone_grid``
|
|
904
|
+
(``is_zone_grid``); each label carries an ``is_zone_label`` rider so it is exempt from the
|
|
905
|
+
page-bounds lint (it legitimately sits outside the drawable) — but NOT from overlap lint."""
|
|
906
|
+
from build123d_drafting import Note
|
|
907
|
+
|
|
908
|
+
cols, rows = _zone_divisions(a.PAGE_W, a.PAGE_H)
|
|
909
|
+
x0, y0, x1, y1 = _MARGIN, _MARGIN, a.PAGE_W - _MARGIN, a.PAGE_H - _MARGIN
|
|
910
|
+
cw, rh = (x1 - x0) / cols, (y1 - y0) / rows
|
|
911
|
+
band = _MARGIN
|
|
912
|
+
tick = min(3.0, band * 0.6)
|
|
913
|
+
draft = draft_preset(
|
|
914
|
+
font_size=dwg.draft.font_size * 0.8,
|
|
915
|
+
decimal_precision=dwg.draft.decimal_precision,
|
|
916
|
+
font_path=PLEX_SANS_CONDENSED,
|
|
917
|
+
)
|
|
918
|
+
ticks = []
|
|
919
|
+
for i in range(1, cols): # interior column boundaries → ticks on top + bottom edges
|
|
920
|
+
xb = x0 + i * cw
|
|
921
|
+
ticks.append(Edge.make_line(Vector(xb, y0, 0), Vector(xb, y0 - tick, 0)))
|
|
922
|
+
ticks.append(Edge.make_line(Vector(xb, y1, 0), Vector(xb, y1 + tick, 0)))
|
|
923
|
+
for j in range(1, rows): # interior row boundaries → ticks on left + right edges
|
|
924
|
+
yb = y0 + j * rh
|
|
925
|
+
ticks.append(Edge.make_line(Vector(x0, yb, 0), Vector(x0 - tick, yb, 0)))
|
|
926
|
+
ticks.append(Edge.make_line(Vector(x1, yb, 0), Vector(x1 + tick, yb, 0)))
|
|
927
|
+
grid = Compound(children=ticks)
|
|
928
|
+
grid.is_zone_grid = True
|
|
929
|
+
dwg.add(grid, "zone_grid")
|
|
930
|
+
|
|
931
|
+
def _label(text, cx, cy, name):
|
|
932
|
+
note = Note(text, (cx, cy), draft, align=(Align.CENTER, Align.CENTER))
|
|
933
|
+
note.is_zone_label = True
|
|
934
|
+
dwg.add(note, name)
|
|
935
|
+
|
|
936
|
+
for i in range(cols): # numbers 1.. left→right, in the bottom + top bands
|
|
937
|
+
cx = x0 + (i + 0.5) * cw
|
|
938
|
+
_label(str(i + 1), cx, y0 - band / 2, f"zone_num_b_{i}")
|
|
939
|
+
_label(str(i + 1), cx, y1 + band / 2, f"zone_num_t_{i}")
|
|
940
|
+
for j in range(rows): # letters A.. top→bottom, in the left + right bands
|
|
941
|
+
cy = y1 - (j + 0.5) * rh
|
|
942
|
+
_label(_ZONE_LETTERS[j], x0 - band / 2, cy, f"zone_ltr_l_{j}")
|
|
943
|
+
_label(_ZONE_LETTERS[j], x1 + band / 2, cy, f"zone_ltr_r_{j}")
|
|
944
|
+
|
|
945
|
+
|
|
802
946
|
def _iso_bbox(dwg):
|
|
803
947
|
"""(min_x, min_y, max_x, max_y) of the placed iso view, hidden lines included."""
|
|
804
948
|
return dwg.view_bounds("iso")
|
|
@@ -29,6 +29,7 @@ from draftwright._core import (
|
|
|
29
29
|
_MIN_RENDER_MM,
|
|
30
30
|
_MIN_VIEW_MM,
|
|
31
31
|
Analysis,
|
|
32
|
+
_content_margin,
|
|
32
33
|
_legible_steps,
|
|
33
34
|
_Projector,
|
|
34
35
|
)
|
|
@@ -409,6 +410,7 @@ def _validate_explicit_scale(
|
|
|
409
410
|
strips_i,
|
|
410
411
|
layout_section,
|
|
411
412
|
layout_table_sizes,
|
|
413
|
+
margin=_MARGIN,
|
|
412
414
|
) -> None:
|
|
413
415
|
"""Enforce the two scale floors when the caller pinned an explicit *scale* (#489, #590 split
|
|
414
416
|
of :func:`_analyse`). An explicit scale is the user's call — honour it, subject to:
|
|
@@ -441,6 +443,7 @@ def _validate_explicit_scale(
|
|
|
441
443
|
strips=strips_i,
|
|
442
444
|
section=layout_section,
|
|
443
445
|
table_sizes=layout_table_sizes,
|
|
446
|
+
margin=margin,
|
|
444
447
|
)
|
|
445
448
|
# Warn only when omitting the scale would truly give a legible fit (auto scale itself is
|
|
446
449
|
# legible) but the requested scale is below the floor. A part illegible at every
|
|
@@ -474,11 +477,20 @@ def _analyse(
|
|
|
474
477
|
date="",
|
|
475
478
|
revision="A",
|
|
476
479
|
company="",
|
|
480
|
+
frame: bool = False,
|
|
481
|
+
projection: str | None = None,
|
|
482
|
+
zones: bool = False,
|
|
477
483
|
) -> Analysis:
|
|
478
484
|
"""Load STEP or use a build123d Shape, analyse geometry, compute layout.
|
|
479
485
|
|
|
480
486
|
Returns an :class:`Analysis`.
|
|
481
487
|
"""
|
|
488
|
+
# The zone-grid ruler (#768) draws its ticks on the frame, so it implies one.
|
|
489
|
+
frame = frame or zones
|
|
490
|
+
# The content margin — raised by the sheet-frame band (#767) so scale/page selection and
|
|
491
|
+
# placement both reserve room for the border. Computed up front so the choose_scale inside
|
|
492
|
+
# step-count convergence sees it too.
|
|
493
|
+
margin = _content_margin(frame)
|
|
482
494
|
if isinstance(step_file, Shape):
|
|
483
495
|
part = step_file
|
|
484
496
|
src = "build123d object"
|
|
@@ -609,6 +621,7 @@ def _analyse(
|
|
|
609
621
|
strips=strips_i,
|
|
610
622
|
section=layout_section,
|
|
611
623
|
table_sizes=layout_table_sizes,
|
|
624
|
+
margin=margin,
|
|
612
625
|
)
|
|
613
626
|
|
|
614
627
|
(SCALE, PAGE_W, PAGE_H, TB_W), strips_i, n_for_sizing = _converge_step_sizing(
|
|
@@ -628,9 +641,10 @@ def _analyse(
|
|
|
628
641
|
strips_i,
|
|
629
642
|
layout_section,
|
|
630
643
|
layout_table_sizes,
|
|
644
|
+
margin=margin,
|
|
631
645
|
)
|
|
632
646
|
DIM_PAD = _DIM_PAD
|
|
633
|
-
margin
|
|
647
|
+
# margin was computed up front (_content_margin(frame)) so scale selection already saw it.
|
|
634
648
|
# Refine: apply the same legibility gate _auto_annotate uses for dim_step.
|
|
635
649
|
n_steps = len(_legible_steps(step_zs, bb.min.Z, SCALE)[0])
|
|
636
650
|
strips = _measure_strips(
|
|
@@ -656,6 +670,7 @@ def _analyse(
|
|
|
656
670
|
n_steps,
|
|
657
671
|
section=layout_section,
|
|
658
672
|
table_sizes=layout_table_sizes,
|
|
673
|
+
margin=margin,
|
|
659
674
|
)
|
|
660
675
|
fv_hw = _g.fv_hw
|
|
661
676
|
fv_hh = _g.fv_hh
|
|
@@ -778,6 +793,9 @@ def _analyse(
|
|
|
778
793
|
date=date,
|
|
779
794
|
revision=revision,
|
|
780
795
|
company=company,
|
|
796
|
+
frame=frame,
|
|
797
|
+
projection=projection,
|
|
798
|
+
zones=zones,
|
|
781
799
|
out=out,
|
|
782
800
|
pmi=pmi_records,
|
|
783
801
|
pmi_mode=pmi,
|
|
@@ -21,7 +21,10 @@ from draftwright._core import (
|
|
|
21
21
|
_TABULATE_MIN_HOLES,
|
|
22
22
|
Analysis,
|
|
23
23
|
HoleRef,
|
|
24
|
+
_add_projection_symbol,
|
|
25
|
+
_add_sheet_frame,
|
|
24
26
|
_add_title_block,
|
|
27
|
+
_add_zone_grid,
|
|
25
28
|
_concentric_with_axis,
|
|
26
29
|
_fmt,
|
|
27
30
|
_iso_bbox,
|
|
@@ -125,6 +128,9 @@ _PASS_SEQUENCE: tuple[str, ...] = (
|
|
|
125
128
|
"details",
|
|
126
129
|
"title_block",
|
|
127
130
|
"tabulate",
|
|
131
|
+
"sheet_frame",
|
|
132
|
+
"zone_grid",
|
|
133
|
+
"projection_symbol",
|
|
128
134
|
)
|
|
129
135
|
|
|
130
136
|
|
|
@@ -508,6 +514,22 @@ def _auto_annotate(dwg, a: Analysis, *, detail_view: bool = False):
|
|
|
508
514
|
def _s_title_block():
|
|
509
515
|
_add_title_block(dwg, a)
|
|
510
516
|
|
|
517
|
+
def _s_sheet_frame():
|
|
518
|
+
# The sheet border, drawn LAST (#767) — gated on the frame opt-in; content already
|
|
519
|
+
# reserved room via the raised a.margin, so this only draws.
|
|
520
|
+
if a.frame:
|
|
521
|
+
_add_sheet_frame(dwg, a)
|
|
522
|
+
|
|
523
|
+
def _s_zone_grid():
|
|
524
|
+
# ISO 5457 zone-grid border ruler (#768), on the frame (a.zones implies a.frame).
|
|
525
|
+
if a.zones:
|
|
526
|
+
_add_zone_grid(dwg, a)
|
|
527
|
+
|
|
528
|
+
def _s_projection_symbol():
|
|
529
|
+
# ISO 5456-2 projection-method glyph (#769) in the reserved title-block band.
|
|
530
|
+
if a.projection:
|
|
531
|
+
_add_projection_symbol(dwg, a)
|
|
532
|
+
|
|
511
533
|
def _s_tabulate():
|
|
512
534
|
# Escalate to a hole table when the plan view is too dense to dimension
|
|
513
535
|
# every hole — runs last so the table avoids every placed annotation
|
|
@@ -545,6 +567,9 @@ def _auto_annotate(dwg, a: Analysis, *, detail_view: bool = False):
|
|
|
545
567
|
"details": _s_details,
|
|
546
568
|
"title_block": _s_title_block,
|
|
547
569
|
"tabulate": _s_tabulate,
|
|
570
|
+
"sheet_frame": _s_sheet_frame,
|
|
571
|
+
"zone_grid": _s_zone_grid,
|
|
572
|
+
"projection_symbol": _s_projection_symbol,
|
|
548
573
|
}
|
|
549
574
|
)
|
|
550
575
|
if ctx.trace is not None: # snapshot the run's escalations into the trace (#736)
|
|
@@ -29,7 +29,10 @@ from draftwright._core import (
|
|
|
29
29
|
_PAGE_SIZES,
|
|
30
30
|
_SCALES,
|
|
31
31
|
Analysis,
|
|
32
|
+
_add_projection_symbol,
|
|
33
|
+
_add_sheet_frame,
|
|
32
34
|
_add_title_block,
|
|
35
|
+
_add_zone_grid,
|
|
33
36
|
_iso_bbox,
|
|
34
37
|
_log,
|
|
35
38
|
_parse_page,
|
|
@@ -336,6 +339,12 @@ def _assemble(
|
|
|
336
339
|
else:
|
|
337
340
|
_fit_iso_view(dwg, a, annotate=False)
|
|
338
341
|
_add_title_block(dwg, a)
|
|
342
|
+
if a.frame: # sheet border, drawn last (#767) — auto path adds it via the orchestrator
|
|
343
|
+
_add_sheet_frame(dwg, a)
|
|
344
|
+
if a.zones: # zone-grid ruler (#768), on the frame
|
|
345
|
+
_add_zone_grid(dwg, a)
|
|
346
|
+
if a.projection: # projection-method glyph (#769) — auto path adds it via the orchestrator
|
|
347
|
+
_add_projection_symbol(dwg, a)
|
|
339
348
|
return dwg
|
|
340
349
|
|
|
341
350
|
|
|
@@ -409,6 +418,7 @@ def _repack(
|
|
|
409
418
|
section=a.layout_section,
|
|
410
419
|
table_sizes=a.layout_table_sizes,
|
|
411
420
|
warn_no_iso=False,
|
|
421
|
+
margin=a.margin,
|
|
412
422
|
)
|
|
413
423
|
|
|
414
424
|
candidates = _repack_candidates(a, scale, page)
|
|
@@ -603,6 +613,9 @@ def build_drawing(
|
|
|
603
613
|
date: str = "",
|
|
604
614
|
revision: str = "A",
|
|
605
615
|
company: str = "",
|
|
616
|
+
frame: bool = False,
|
|
617
|
+
projection: str | None = None,
|
|
618
|
+
zones: bool = False,
|
|
606
619
|
) -> Drawing:
|
|
607
620
|
"""Build a customisable 4-view :class:`Drawing` without exporting it.
|
|
608
621
|
|
|
@@ -690,6 +703,9 @@ def build_drawing(
|
|
|
690
703
|
date=date,
|
|
691
704
|
revision=revision,
|
|
692
705
|
company=company,
|
|
706
|
+
frame=frame,
|
|
707
|
+
projection=projection,
|
|
708
|
+
zones=zones,
|
|
693
709
|
)
|
|
694
710
|
|
|
695
711
|
# Pass 1: place + annotate from the estimated layout, then measure the real
|
|
@@ -754,6 +770,9 @@ def make_drawing(
|
|
|
754
770
|
date: str = "",
|
|
755
771
|
revision: str = "A",
|
|
756
772
|
company: str = "",
|
|
773
|
+
frame: bool = False,
|
|
774
|
+
projection: str | None = None,
|
|
775
|
+
zones: bool = False,
|
|
757
776
|
) -> tuple[str, str]:
|
|
758
777
|
"""Generate a 4-view technical drawing from a STEP file or build123d object.
|
|
759
778
|
|
|
@@ -799,6 +818,9 @@ def make_drawing(
|
|
|
799
818
|
date=date,
|
|
800
819
|
revision=revision,
|
|
801
820
|
company=company,
|
|
821
|
+
frame=frame,
|
|
822
|
+
projection=projection,
|
|
823
|
+
zones=zones,
|
|
802
824
|
).export()
|
|
803
825
|
assert svg_path is not None and dxf_path is not None # export() writes both by default
|
|
804
826
|
return svg_path, dxf_path
|
|
@@ -106,6 +106,15 @@ def main(
|
|
|
106
106
|
date: str = typer.Option("", help="Date for the title block"),
|
|
107
107
|
revision: str = typer.Option("A", help="Revision for the title block"),
|
|
108
108
|
company: str = typer.Option("", help="Company / legal owner for the title block"),
|
|
109
|
+
frame: bool = typer.Option(
|
|
110
|
+
False, "--frame", help="Draw a sheet border; content reserves clearance inside it"
|
|
111
|
+
),
|
|
112
|
+
projection: str = typer.Option(
|
|
113
|
+
"", "--projection", help="Projection-method symbol: 'third' or 'first' (default: none)"
|
|
114
|
+
),
|
|
115
|
+
zones: bool = typer.Option(
|
|
116
|
+
False, "--zones", help="Draw the ISO 5457 zone-grid border ruler (implies --frame)"
|
|
117
|
+
),
|
|
109
118
|
scale: float | None = typer.Option(
|
|
110
119
|
None, help="Drawing-scale override, e.g. 5 for 5:1 or 0.5 for 1:2 (default: auto)"
|
|
111
120
|
),
|
|
@@ -186,6 +195,9 @@ def main(
|
|
|
186
195
|
date=date,
|
|
187
196
|
revision=revision,
|
|
188
197
|
company=company,
|
|
198
|
+
frame=frame,
|
|
199
|
+
zones=zones,
|
|
200
|
+
projection=projection or None,
|
|
189
201
|
part_expr=seam,
|
|
190
202
|
formats=tuple(formats),
|
|
191
203
|
)
|
|
@@ -203,6 +215,9 @@ def main(
|
|
|
203
215
|
date=date,
|
|
204
216
|
revision=revision,
|
|
205
217
|
company=company,
|
|
218
|
+
frame=frame,
|
|
219
|
+
zones=zones,
|
|
220
|
+
projection=projection or None,
|
|
206
221
|
pmi=pmi.value,
|
|
207
222
|
formats=tuple(formats),
|
|
208
223
|
)
|
|
@@ -242,6 +257,9 @@ def main(
|
|
|
242
257
|
date=date,
|
|
243
258
|
revision=revision,
|
|
244
259
|
company=company,
|
|
260
|
+
frame=frame,
|
|
261
|
+
projection=projection or None,
|
|
262
|
+
zones=zones,
|
|
245
263
|
)
|
|
246
264
|
for path in _emit(dwg, formats):
|
|
247
265
|
print(path)
|
|
@@ -435,6 +435,7 @@ def _fits(
|
|
|
435
435
|
pack_iso_2d: bool = False,
|
|
436
436
|
section: bool = False,
|
|
437
437
|
table_sizes=(),
|
|
438
|
+
margin: float = _MARGIN,
|
|
438
439
|
) -> bool:
|
|
439
440
|
"""True if the composed 4-view footprint fits the page at this scale.
|
|
440
441
|
|
|
@@ -456,12 +457,24 @@ def _fits(
|
|
|
456
457
|
section=section,
|
|
457
458
|
table_sizes=table_sizes,
|
|
458
459
|
warn_no_iso=False,
|
|
460
|
+
margin=margin,
|
|
459
461
|
)
|
|
460
462
|
return bool(g.fits if pack_iso_2d else g.auto_fits)
|
|
461
463
|
|
|
462
464
|
|
|
463
465
|
def _bisect_fit_scale(
|
|
464
|
-
x_size,
|
|
466
|
+
x_size,
|
|
467
|
+
y_size,
|
|
468
|
+
z_size,
|
|
469
|
+
pw,
|
|
470
|
+
ph,
|
|
471
|
+
tb,
|
|
472
|
+
n_steps,
|
|
473
|
+
strips,
|
|
474
|
+
pack_iso_2d,
|
|
475
|
+
section=False,
|
|
476
|
+
table_sizes=(),
|
|
477
|
+
margin=_MARGIN,
|
|
465
478
|
):
|
|
466
479
|
"""Largest scale at which the 4-view layout fits ``(pw, ph)``, found by bisection —
|
|
467
480
|
the layout is monotone in scale (a smaller scale never fits worse). Used only as the
|
|
@@ -485,6 +498,7 @@ def _bisect_fit_scale(
|
|
|
485
498
|
pack_iso_2d=pack_iso_2d,
|
|
486
499
|
section=section,
|
|
487
500
|
table_sizes=table_sizes,
|
|
501
|
+
margin=margin,
|
|
488
502
|
):
|
|
489
503
|
lo = mid
|
|
490
504
|
else:
|
|
@@ -502,6 +516,7 @@ def choose_scale(
|
|
|
502
516
|
strips: StripDepths | None = None,
|
|
503
517
|
section: bool = False,
|
|
504
518
|
table_sizes=(),
|
|
519
|
+
margin: float = _MARGIN,
|
|
505
520
|
) -> tuple:
|
|
506
521
|
"""Return (SCALE, PAGE_W, PAGE_H, TB_W) for a 4-view layout.
|
|
507
522
|
|
|
@@ -539,6 +554,7 @@ def choose_scale(
|
|
|
539
554
|
pack_iso_2d=True,
|
|
540
555
|
section=section,
|
|
541
556
|
table_sizes=table_sizes,
|
|
557
|
+
margin=margin,
|
|
542
558
|
):
|
|
543
559
|
_log.warning(
|
|
544
560
|
"Requested scale %s on %s page may not fit the 4-view layout", scale, page
|
|
@@ -565,6 +581,7 @@ def choose_scale(
|
|
|
565
581
|
pack_iso_2d=pack_iso_2d,
|
|
566
582
|
section=section,
|
|
567
583
|
table_sizes=table_sizes,
|
|
584
|
+
margin=margin,
|
|
568
585
|
):
|
|
569
586
|
return cand
|
|
570
587
|
# The ISO 5455 ladder exhausted with no standard fit (a part too large even for
|
|
@@ -586,6 +603,7 @@ def choose_scale(
|
|
|
586
603
|
pack_iso_2d,
|
|
587
604
|
section,
|
|
588
605
|
table_sizes,
|
|
606
|
+
margin,
|
|
589
607
|
)
|
|
590
608
|
if s is not None:
|
|
591
609
|
_log.warning(
|
|
@@ -758,6 +776,7 @@ def _layout_geometry(
|
|
|
758
776
|
section: bool = False,
|
|
759
777
|
table_sizes=(),
|
|
760
778
|
warn_no_iso=True,
|
|
779
|
+
margin: float = _MARGIN,
|
|
761
780
|
):
|
|
762
781
|
"""Compute the 4-view layout geometry for a part at a given scale/page.
|
|
763
782
|
|
|
@@ -770,7 +789,9 @@ def _layout_geometry(
|
|
|
770
789
|
step-count estimate (used during scale selection before strips are
|
|
771
790
|
measured); otherwise the measured strip depths are used.
|
|
772
791
|
"""
|
|
773
|
-
margin
|
|
792
|
+
# margin is a parameter (default _MARGIN) so a reserved content margin — e.g. the
|
|
793
|
+
# #767 sheet-frame band — flows through BOTH scale selection and placement, which
|
|
794
|
+
# share this one authority. Default keeps every existing caller byte-identical.
|
|
774
795
|
DIM_PAD = _DIM_PAD
|
|
775
796
|
bbox_max = max(x_size, y_size, z_size)
|
|
776
797
|
fv_hw = x_size * scale / 2
|
|
@@ -278,7 +278,13 @@ def lint_drawing(
|
|
|
278
278
|
|
|
279
279
|
boxes: list = []
|
|
280
280
|
for item in items:
|
|
281
|
-
|
|
281
|
+
# The sheet frame (#767) spans the page by design; a None box excludes it from every
|
|
282
|
+
# pairwise overlap (like a centerline), so it doesn't "overlap" every annotation.
|
|
283
|
+
if (
|
|
284
|
+
getattr(item, "is_centerline", False)
|
|
285
|
+
or getattr(item, "is_sheet_frame", False)
|
|
286
|
+
or getattr(item, "is_zone_grid", False) # border ticks span the frame, like the frame
|
|
287
|
+
):
|
|
282
288
|
boxes.append(None)
|
|
283
289
|
continue
|
|
284
290
|
boxes.append(_label_box(item))
|
|
@@ -333,6 +339,13 @@ def lint_drawing(
|
|
|
333
339
|
# (#701: unguarded — _ann_box absorbs the fragile measure; the rest is arithmetic.)
|
|
334
340
|
if page_bbox is not None:
|
|
335
341
|
for item in items:
|
|
342
|
+
if (
|
|
343
|
+
getattr(item, "is_sheet_frame", False)
|
|
344
|
+
or getattr(item, "is_zone_grid", False)
|
|
345
|
+
or getattr(item, "is_zone_label", False)
|
|
346
|
+
):
|
|
347
|
+
# Frame + zone ruler sit ON / outside the drawable boundary by design (#767/#768).
|
|
348
|
+
continue
|
|
336
349
|
bb = _ann_box(item, box_cache)
|
|
337
350
|
if bb is None:
|
|
338
351
|
continue
|
|
@@ -498,6 +511,8 @@ def _lint_view_shapes(
|
|
|
498
511
|
continue # a datum target sits on the part face by definition
|
|
499
512
|
if getattr(ann, "is_section_hatch", False):
|
|
500
513
|
continue # hatching is intentionally inside the section view
|
|
514
|
+
if getattr(ann, "is_sheet_frame", False) or getattr(ann, "is_zone_grid", False):
|
|
515
|
+
continue # sheet border / zone ticks span the page, enclosing every view (#767/#768)
|
|
501
516
|
# #701: unguarded — _label_bbox/_ann_box/_view_edge_entries absorb the
|
|
502
517
|
# fragile reads; a bug in the check itself must fail loudly.
|
|
503
518
|
label_box = _label_bbox(ann, warned)
|
|
@@ -374,6 +374,9 @@ class Sheet:
|
|
|
374
374
|
date=None,
|
|
375
375
|
revision=None,
|
|
376
376
|
company=None,
|
|
377
|
+
frame=None,
|
|
378
|
+
projection=None,
|
|
379
|
+
zones=None,
|
|
377
380
|
):
|
|
378
381
|
self._part = part
|
|
379
382
|
self._features: list = []
|
|
@@ -404,6 +407,9 @@ class Sheet:
|
|
|
404
407
|
("date", date),
|
|
405
408
|
("revision", revision),
|
|
406
409
|
("company", company),
|
|
410
|
+
("frame", frame),
|
|
411
|
+
("projection", projection),
|
|
412
|
+
("zones", zones),
|
|
407
413
|
):
|
|
408
414
|
if _v is not None:
|
|
409
415
|
self._opts[_k] = _v
|