draftwright 0.1.2__tar.gz → 0.1.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: draftwright
3
- Version: 0.1.2
3
+ Version: 0.1.3
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "draftwright"
7
- version = "0.1.2"
7
+ version = "0.1.3"
8
8
  description = "Automated technical-drawing generation for build123d"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -810,6 +810,29 @@ def _analyse(step_file, title, number, tolerance, drawn_by, out, scale=None, pag
810
810
  ISO_X = sv_right + right_avail / 2
811
811
  ISO_Y = PV_Y
812
812
 
813
+ # When the standard iso zone (right of SV) is narrower than the natural iso
814
+ # extent, check whether the upper-right zone — right of FV/PV and above the SV
815
+ # y-range — offers more room. This zone shares no y-range with the SV, so the
816
+ # iso can sit there without conflicting with SV annotations.
817
+ _iso_natural = bbox_max * SCALE * 0.7
818
+ _ur_left = FV_X + fv_hw + gap_fv_sv # = sv_left_edge; clears FV/PV right strips
819
+ _sv_top = FV_Y + fv_hh # SV_Y == FV_Y; sv_top == FV_Y + fv_hh
820
+ _ur_bottom = _sv_top + DIM_PAD
821
+ _ur_w = max(0.0, iso_right_limit - _ur_left)
822
+ _ur_h = max(0.0, (PAGE_H - margin) - _ur_bottom)
823
+ _std_min = min(right_avail, PAGE_H - 2 * margin)
824
+ _ur_min = min(_ur_w, _ur_h)
825
+ if _std_min < _iso_natural and _ur_min > _std_min:
826
+ ISO_X = (_ur_left + iso_right_limit) / 2
827
+ ISO_Y = (_ur_bottom + PAGE_H - margin) / 2
828
+ iso_left_limit = _ur_left
829
+ iso_bottom_limit = _ur_bottom
830
+ iso_in_upper_right = True
831
+ else:
832
+ iso_left_limit = sv_right
833
+ iso_bottom_limit = margin
834
+ iso_in_upper_right = False
835
+
813
836
  # ------------------------------------------------------------------
814
837
  # Strip / zone construction.
815
838
  # Phase 1: defines regions only — annotation functions still use their
@@ -909,6 +932,9 @@ def _analyse(step_file, title, number, tolerance, drawn_by, out, scale=None, pag
909
932
  SV_Y=SV_Y,
910
933
  ISO_X=ISO_X,
911
934
  ISO_Y=ISO_Y,
935
+ iso_left_limit=iso_left_limit,
936
+ iso_bottom_limit=iso_bottom_limit,
937
+ iso_in_upper_right=iso_in_upper_right,
912
938
  # View half-extents in page units (convenient for strip arithmetic)
913
939
  fv_hw=fv_hw,
914
940
  fv_hh=fv_hh,
@@ -1259,7 +1285,15 @@ def _auto_annotate(dwg, a):
1259
1285
  # the limit (dims already placed may overlap the iso view).
1260
1286
  _iso_x0, _, _, _ = _iso_bbox(dwg)
1261
1287
  _iso_x_limit = _iso_x0 - 4
1262
- for _rs in (a.fv_zones.right, a.pv_zones.right, a.sv_zones.right):
1288
+ # When the iso sits above the SV (upper-right zone), the SV right strip shares
1289
+ # no y-range with the iso, so tightening sv_zones.right by iso_x would set
1290
+ # outer_limit below the strip anchor and break all SV annotation allocations.
1291
+ _right_strips = (
1292
+ (a.fv_zones.right, a.pv_zones.right)
1293
+ if a.iso_in_upper_right
1294
+ else (a.fv_zones.right, a.pv_zones.right, a.sv_zones.right)
1295
+ )
1296
+ for _rs in _right_strips:
1263
1297
  _rs.outer_limit = min(_rs.outer_limit, _iso_x_limit)
1264
1298
  if _rs._cursor >= _iso_x_limit:
1265
1299
  _log.warning(
@@ -2662,25 +2696,31 @@ def _project_iso(dwg, a, scale, shape_s=None):
2662
2696
  dwg._coords["iso"] = ViewCoordinates(axes, a.ISO_X, a.ISO_Y, a.cx, a.cy, a.cz, scale)
2663
2697
 
2664
2698
 
2665
- def _fit_iso_view(dwg, a):
2666
- """Shrink the iso view to fit its page region, captioning it NTS (#75).
2699
+ def _fit_iso_view(dwg, a, annotate: bool = True):
2700
+ """Scale the iso view to fill its page zone, captioning it NTS when the
2701
+ scale differs from sheet scale. Pass ``annotate=False`` to suppress the
2702
+ NTS note (used when ``auto_dims=False``).
2703
+
2704
+ The iso is always centred at (ISO_X, ISO_Y) which sits at the centre of
2705
+ the available zone. The projection is linear, so the factor needed to
2706
+ fill the zone can be computed from the measured extents without iteration.
2667
2707
 
2668
- The layout reserves ~0.7 × bbox_max for the iso column, but the true
2669
- projected extent can be wider (long prismatic parts), pushing the iso past
2670
- the page edge or into the side view's dimension space. When the projected
2671
- iso bbox overflows the region, re-project at a clean fraction of sheet
2672
- scale and add an "ISO VIEW (NTS)" caption below it.
2708
+ - Overflow (needed < 1): shrink with 2 % safety margin.
2709
+ - Under-fill (needed > 1): grow to 90 % of zone, leaving breathing room.
2710
+ - Within 5 % of sheet scale: leave as-is (no NTS label).
2673
2711
  """
2674
- region = (a.sv_right, a.margin, a.iso_right_limit, a.PAGE_H - a.margin)
2712
+ # Use the precomputed iso zone limits. When iso is in the upper-right zone,
2713
+ # any section view sits below the iso's y-range so its x-extent doesn't
2714
+ # constrain the iso region.
2715
+ region_left = a.iso_left_limit
2716
+ if not a.iso_in_upper_right and "section_aa" in dwg.views:
2717
+ sec_vis, sec_hid = dwg.views["section_aa"]
2718
+ sec_right = sec_vis.bounding_box().max.X
2719
+ if sec_hid:
2720
+ sec_right = max(sec_right, sec_hid.bounding_box().max.X)
2721
+ region_left = max(region_left, sec_right + 4)
2722
+ region = (region_left, a.iso_bottom_limit, a.iso_right_limit, a.PAGE_H - a.margin)
2675
2723
  bb = _iso_bbox(dwg)
2676
- # Exact check (no tolerance): the lint's view_out_of_bounds is exact, so
2677
- # accepting a sub-tolerance overflow here would pass the fit yet fail lint.
2678
- if _bbox_within(bb, region, tol=0.0):
2679
- return
2680
- # Orthographic projection is linear and the view centre maps to
2681
- # (ISO_X, ISO_Y), so each bbox side's offset from the centre scales
2682
- # exactly with the shape scale — the factor needed to fit can be computed
2683
- # from the measured extents, costing a single re-projection.
2684
2724
  ratios = [
2685
2725
  avail / extent
2686
2726
  for extent, avail in (
@@ -2692,24 +2732,32 @@ def _fit_iso_view(dwg, a):
2692
2732
  if extent > 0
2693
2733
  ]
2694
2734
  needed = min(ratios, default=1.0)
2695
- # Apply a 2 % safety margin and floor to 4 decimal places to avoid
2696
- # floating-point creep past the region boundary. The iso is NTS so
2697
- # there is no need to constrain to "clean" fractions.
2698
- factor = math.floor(needed * 0.98 * 10000) / 10000
2735
+ if needed >= 1.0:
2736
+ # Iso fits; grow to 90 % of zone leaves comfortable breathing room.
2737
+ margin_pct = 0.90
2738
+ else:
2739
+ # Iso overflows; shrink to just fit with 2 % safety margin.
2740
+ margin_pct = 0.98
2741
+ factor = math.floor(needed * margin_pct * 10000) / 10000
2742
+ if needed >= 1.0:
2743
+ factor = max(factor, 1.0) # grow branch must never shrink
2744
+ if abs(factor - 1.0) < 0.05:
2745
+ return # within 5 % of sheet scale — no rescale, no NTS label
2699
2746
  _project_iso(dwg, a, a.SCALE * factor)
2700
2747
  bb = _iso_bbox(dwg)
2701
- if not _bbox_within(bb, region):
2748
+ if factor < 1.0 and not _bbox_within(bb, region):
2702
2749
  _log.warning("Iso view still overflows its page region at %g× sheet scale", factor)
2703
- font = dwg.draft.font_size
2704
- dwg.add(
2705
- Note(
2706
- "ISO VIEW (NTS)",
2707
- (a.ISO_X, max(bb[1] - 2 * font, a.margin + font)),
2708
- dwg.draft,
2709
- ),
2710
- "note_iso_nts",
2711
- )
2712
- _log.info("Iso view shrunk to %g× sheet scale (NTS)", factor)
2750
+ if annotate:
2751
+ font = dwg.draft.font_size
2752
+ dwg.add(
2753
+ Note(
2754
+ "ISO VIEW (NTS)",
2755
+ (a.ISO_X, max(bb[1] - 2 * font, a.margin + font)),
2756
+ dwg.draft,
2757
+ ),
2758
+ "note_iso_nts",
2759
+ )
2760
+ _log.info("Iso view scaled to %g× sheet scale%s", factor, " (NTS)" if annotate else "")
2713
2761
 
2714
2762
 
2715
2763
  def build_drawing(
@@ -2779,11 +2827,24 @@ def build_drawing(
2779
2827
  dwg.add_view("plan", part_s, (cxs, cys, czs + dist), (0, 1, 0), (a.PV_X, a.PV_Y), scaled=True)
2780
2828
  dwg.add_view("side", part_s, (cxs + dist, cys, czs), (0, 0, 1), (a.SV_X, a.SV_Y), scaled=True)
2781
2829
  _project_iso(dwg, a, a.SCALE, shape_s=part_s)
2782
- _fit_iso_view(dwg, a)
2783
2830
 
2784
2831
  if auto_dims:
2832
+ # Snapshot outer_limits before _auto_annotate tightens them against the
2833
+ # initial (possibly overflowing) iso. After _fit_iso_view rescales the
2834
+ # iso we restore all three right strips to min(original, final_iso_x_limit)
2835
+ # so each strip reflects actual final geometry, not the transient state.
2836
+ _fv_ol = a.fv_zones.right.outer_limit
2837
+ _pv_ol = a.pv_zones.right.outer_limit
2838
+ _sv_ol = a.sv_zones.right.outer_limit
2785
2839
  _auto_annotate(dwg, a)
2840
+ _fit_iso_view(dwg, a)
2841
+ _final_iso_x_lim = _iso_bbox(dwg)[0] - 4
2842
+ a.fv_zones.right.outer_limit = min(_fv_ol, _final_iso_x_lim)
2843
+ a.pv_zones.right.outer_limit = min(_pv_ol, _final_iso_x_lim)
2844
+ if not a.iso_in_upper_right:
2845
+ a.sv_zones.right.outer_limit = min(_sv_ol, _final_iso_x_lim)
2786
2846
  else:
2847
+ _fit_iso_view(dwg, a, annotate=False)
2787
2848
  _add_title_block(dwg, a)
2788
2849
  return dwg
2789
2850
 
@@ -1135,52 +1135,55 @@ def test_clear_annotations_keep_custom_and_unnamed_removed():
1135
1135
 
1136
1136
 
1137
1137
  @pytest.fixture(scope="module")
1138
- def shrunk_iso_drawing():
1139
- # #75 fixture — NIST CTC-01-like plate at 1:5 on A3: the iso overflows at
1140
- # sheet scale and is auto-shrunk. Module-scoped; tests must not mutate it.
1138
+ def ctc01_a3_drawing():
1139
+ # Fixture — NIST CTC-01-like plate at 1:5 on A3. Module-scoped; tests must not mutate it.
1141
1140
  return build_drawing(Box(800, 450, 150), scale=0.2, page="A3")
1142
1141
 
1143
1142
 
1144
1143
  @pytest.mark.timeout(120)
1145
- def test_iso_overflow_shrinks_with_nts_note(shrunk_iso_drawing):
1146
- # #75 — at sheet scale the iso would run past the A3 page edge; it must be
1147
- # re-projected smaller and captioned NTS.
1144
+ def test_ctc01_iso_uses_upper_right_zone(ctc01_a3_drawing):
1145
+ # #75 updated wide/flat part on A3: the iso is repositioned into the upper-right
1146
+ # zone (above the SV, right of FV/PV) where it fits at sheet scale. No NTS label.
1148
1147
  from draftwright.make_drawing import _iso_bbox
1149
1148
 
1150
- dwg = shrunk_iso_drawing
1149
+ dwg = ctc01_a3_drawing
1151
1150
  labels = [getattr(a, "label", "") for a in dwg.annotations]
1152
- assert "ISO VIEW (NTS)" in labels
1151
+ assert "ISO VIEW (NTS)" not in labels # iso now fits at sheet scale — no NTS
1153
1152
  x0, y0, x1, y1 = _iso_bbox(dwg)
1154
1153
  assert (
1155
1154
  x1 <= dwg.page_w - 10 + 0.5 and x0 >= 0 and y0 >= 10 - 0.5 and y1 <= dwg.page_h - 10 + 0.5
1156
1155
  )
1156
+ # iso must be significantly larger than the old 65 mm (shrunken) view
1157
+ assert (x1 - x0) > 100
1157
1158
 
1158
1159
 
1159
1160
  @pytest.mark.timeout(120)
1160
- def test_shrunk_iso_keeps_world_to_page_mapping(shrunk_iso_drawing):
1161
- # After the NTS shrink, dwg.at("iso", ...) must still map world points to
1162
- # the page: the centroid lands on the view centre and offsets scale by the
1163
- # shrunk view scale, not the sheet scale.
1164
- dwg = shrunk_iso_drawing
1161
+ def test_ctc01_iso_world_to_page_mapping(ctc01_a3_drawing):
1162
+ # dwg.at("iso", ...) must map world points to page even after the iso is
1163
+ # repositioned to the upper-right zone (still projected at sheet scale).
1164
+ dwg = ctc01_a3_drawing
1165
1165
  cx, cy, cz = dwg.centroid
1166
1166
  centre = dwg.at("iso", cx, cy, cz)
1167
1167
  vis, _hid = dwg.views["iso"]
1168
1168
  bb = vis.bounding_box()
1169
1169
  assert bb.min.X < centre[0] < bb.max.X and bb.min.Y < centre[1] < bb.max.Y
1170
- # World +Z maps to page +Y; the offset must use the shrunk view scale (not
1171
- # the sheet scale). Derive the actual shrunk scale from _coords so the
1172
- # test does not depend on a specific discretised shrink factor.
1173
- shrunk_scale = dwg._coords["iso"]._scale
1174
- assert shrunk_scale < dwg.scale, "iso should be shrunk below sheet scale"
1170
+ iso_scale = dwg._coords["iso"]._scale
1175
1171
  raised = dwg.at("iso", cx, cy, cz + 100)
1176
- assert raised[1] - centre[1] == pytest.approx(100 * shrunk_scale)
1172
+ assert raised[1] - centre[1] == pytest.approx(100 * iso_scale)
1177
1173
 
1178
1174
 
1179
1175
  @pytest.mark.timeout(60)
1180
- def test_iso_that_fits_is_not_shrunk():
1176
+ def test_iso_stays_within_page_bounds():
1177
+ # Whether scaled up or not, the iso must always lie within the page margin.
1178
+ from draftwright.make_drawing import _iso_bbox
1179
+
1181
1180
  dwg = build_drawing(Box(30, 20, 10))
1182
- labels = [getattr(a, "label", "") for a in dwg.annotations]
1183
- assert "ISO VIEW (NTS)" not in labels
1181
+ x0, y0, x1, y1 = _iso_bbox(dwg)
1182
+ margin = 10
1183
+ assert x0 >= margin - 0.5
1184
+ assert y0 >= margin - 0.5
1185
+ assert x1 <= dwg.page_w - margin + 0.5
1186
+ assert y1 <= dwg.page_h - margin + 0.5
1184
1187
 
1185
1188
 
1186
1189
  @pytest.mark.timeout(60)
File without changes
File without changes
File without changes
File without changes
File without changes