draftwright 0.4.14__tar.gz → 0.4.16__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.
Files changed (305) hide show
  1. {draftwright-0.4.14 → draftwright-0.4.16}/CHANGELOG.md +28 -0
  2. {draftwright-0.4.14 → draftwright-0.4.16}/PKG-INFO +21 -3
  3. {draftwright-0.4.14 → draftwright-0.4.16}/README.md +18 -0
  4. {draftwright-0.4.14 → draftwright-0.4.16}/docs/reference/recogniser-capabilities.md +23 -22
  5. draftwright-0.4.16/docs/reference/sheet.md +174 -0
  6. {draftwright-0.4.14 → draftwright-0.4.16}/pyproject.toml +3 -3
  7. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/_core.py +107 -14
  8. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/_geometry.py +28 -3
  9. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/annotations/_common.py +75 -19
  10. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/annotations/from_model.py +358 -111
  11. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/annotations/holes.py +67 -27
  12. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/annotations/sections.py +5 -2
  13. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/builder.py +475 -64
  14. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/compose.py +117 -34
  15. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/drawing.py +733 -17
  16. draftwright-0.4.16/src/draftwright/export.py +748 -0
  17. draftwright-0.4.16/src/draftwright/inspection_contract.py +243 -0
  18. draftwright-0.4.16/src/draftwright/linting/_registry.py +32 -0
  19. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/linting/channel_coverage.py +19 -4
  20. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/linting/coverage.py +200 -34
  21. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/linting/flat_coverage.py +21 -3
  22. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/linting/hole_coverage.py +41 -2
  23. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/linting/polygonal_stock_coverage.py +16 -2
  24. draftwright-0.4.16/src/draftwright/linting/profiled_bore_coverage.py +238 -0
  25. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/linting/quality.py +13 -3
  26. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/linting/slot_coverage.py +19 -4
  27. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/model/__init__.py +2 -0
  28. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/model/callout.py +75 -8
  29. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/model/compiled.py +73 -11
  30. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/model/declare.py +50 -24
  31. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/model/detect.py +7 -2
  32. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/model/ir.py +214 -3
  33. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/model/planner.py +151 -5
  34. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/projection.py +7 -2
  35. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/recogniser_contract.py +13 -6
  36. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/registry.py +32 -1
  37. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/sheet.py +333 -38
  38. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/sheet_emit.py +288 -31
  39. {draftwright-0.4.14 → draftwright-0.4.16}/tests/conftest.py +26 -0
  40. {draftwright-0.4.14 → draftwright-0.4.16}/tests/refactor_golden/flange_dense.json +70 -89
  41. {draftwright-0.4.14 → draftwright-0.4.16}/tests/refactor_golden/turned_stepped.json +7 -26
  42. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_add_dimension.py +2 -1
  43. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_adr0018_view_selection.py +3 -4
  44. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_audit_differential.py +4 -1
  45. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_compiled_plan_boundary.py +2 -1
  46. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_declared_recognition_gate.py +2 -5
  47. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_detect_once.py +12 -11
  48. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_e2e_standards.py +6 -3
  49. draftwright-0.4.16/tests/test_export_reproducible.py +699 -0
  50. draftwright-0.4.16/tests/test_geometry_graph_spike.py +68 -0
  51. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_import_boundaries.py +3 -0
  52. draftwright-0.4.16/tests/test_inspection_contract.py +158 -0
  53. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1058_wheel_profile.py +5 -1
  54. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1130_view_planning_evidence.py +8 -9
  55. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1217_the_facility_is_shared.py +16 -3
  56. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1260_view_constraints.py +20 -4
  57. draftwright-0.4.16/tests/test_issue_1262_automatic_turned_views.py +85 -0
  58. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1298_manufacturing_requirements.py +3 -1
  59. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1299_page_escalation.py +22 -9
  60. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1308_machined_leader_analytics.py +14 -9
  61. draftwright-0.4.16/tests/test_issue_1332_zone_labels.py +70 -0
  62. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1334_prevent_ink.py +29 -0
  63. draftwright-0.4.16/tests/test_issue_1349_precision_display.py +506 -0
  64. draftwright-0.4.16/tests/test_issue_1350_detected_takeover.py +559 -0
  65. draftwright-0.4.16/tests/test_issue_1351_structured_note_coverage.py +651 -0
  66. draftwright-0.4.16/tests/test_issue_1352_searchable_pdf_text.py +989 -0
  67. draftwright-0.4.16/tests/test_issue_1353_cnc_authoritative_workflow.py +333 -0
  68. draftwright-0.4.16/tests/test_issue_1360_thread_depth.py +197 -0
  69. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_443_grm03_axial_coverage.py +4 -2
  70. draftwright-0.4.16/tests/test_issue_563_placement_intent.py +681 -0
  71. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_798_silhouette_lint.py +8 -2
  72. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_label_provenance.py +2 -15
  73. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_make_drawing.py +145 -29
  74. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_private_test_attr_reads.py +1 -0
  75. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_recogniser_capabilities.py +19 -12
  76. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_recognition_manifest.py +17 -9
  77. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_registry.py +19 -2
  78. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_render_seam.py +9 -4
  79. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_script_detail_parity.py +2 -1
  80. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_sheet_identity_invariant.py +14 -16
  81. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_sheet_of.py +1 -1
  82. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_strip_layout.py +10 -0
  83. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_view_plan.py +8 -2
  84. draftwright-0.4.14/docs/reference/sheet.md +0 -71
  85. draftwright-0.4.14/src/draftwright/export.py +0 -356
  86. draftwright-0.4.14/src/draftwright/linting/profiled_bore_coverage.py +0 -123
  87. draftwright-0.4.14/tests/test_issue_1332_zone_labels.py +0 -49
  88. {draftwright-0.4.14 → draftwright-0.4.16}/.gitignore +0 -0
  89. {draftwright-0.4.14 → draftwright-0.4.16}/LICENSE +0 -0
  90. {draftwright-0.4.14 → draftwright-0.4.16}/docs/adr/README.md +0 -0
  91. {draftwright-0.4.14 → draftwright-0.4.16}/docs/research/1062-repeating-radial-profile-evidence.md +0 -0
  92. {draftwright-0.4.14 → draftwright-0.4.16}/skills/SKILL.md +0 -0
  93. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/__init__.py +0 -0
  94. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/_build_profile.py +0 -0
  95. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/_pmi_part21.py +0 -0
  96. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/_warnings.py +0 -0
  97. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/analysis.py +0 -0
  98. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/annotate.py +0 -0
  99. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/annotations/__init__.py +0 -0
  100. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/annotations/balloons.py +0 -0
  101. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/annotations/gears.py +0 -0
  102. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/annotations/leaders.py +0 -0
  103. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/annotations/orchestrator.py +0 -0
  104. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/audit.py +0 -0
  105. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/cli.py +0 -0
  106. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/evaluation/__init__.py +0 -0
  107. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/evaluation/step_analysis.py +0 -0
  108. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/fits.py +0 -0
  109. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/fonts/IBMPlexMono-Regular.ttf +0 -0
  110. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/fonts/IBMPlexSansCondensed-Regular.ttf +0 -0
  111. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/fonts/LICENSE-IBMPlexMono-OFL.txt +0 -0
  112. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/fonts/LICENSE-IBMPlexSansCondensed-OFL.txt +0 -0
  113. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/fonts/__init__.py +0 -0
  114. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/intents.py +0 -0
  115. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/layout.py +0 -0
  116. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/linting/__init__.py +0 -0
  117. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/linting/evidence.py +0 -0
  118. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/linting/gear_coverage.py +0 -0
  119. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/linting/ink_overlap.py +0 -0
  120. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/linting/issues.py +0 -0
  121. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/linting/pmi_coverage.py +0 -0
  122. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/linting/structural.py +0 -0
  123. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/linting/suggest.py +0 -0
  124. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/make_drawing.py +0 -0
  125. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/model/pmi_lowering.py +0 -0
  126. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/pmi.py +0 -0
  127. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/py.typed +0 -0
  128. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/recognition/__init__.py +0 -0
  129. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/recognition_cache.py +0 -0
  130. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/repair.py +0 -0
  131. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/score.py +0 -0
  132. {draftwright-0.4.14 → draftwright-0.4.16}/src/draftwright/view_plan.py +0 -0
  133. {draftwright-0.4.14 → draftwright-0.4.16}/tests/_kernel.py +0 -0
  134. {draftwright-0.4.14 → draftwright-0.4.16}/tests/_layout_sig.py +0 -0
  135. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/ap242_single_cylinder_diameter.step +0 -0
  136. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/evaluation/README.md +0 -0
  137. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/evaluation/ambiguous-open-semicircle.step +0 -0
  138. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/evaluation/blind-hole.step +0 -0
  139. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/evaluation/corpus-v1.json +0 -0
  140. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/evaluation/plain-block.step +0 -0
  141. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/evaluation/topology-a.step +0 -0
  142. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/evaluation/topology-b.step +0 -0
  143. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/grm03_thumbwheel_drive_screw.step +0 -0
  144. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/grm03_thumbwheel_drive_screw_ap242_pmi.step +0 -0
  145. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/grm04_drive_plate.step +0 -0
  146. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/if_step_flat_across_cylinder.step +0 -0
  147. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/issue_1058_wheel_rh.step +0 -0
  148. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/issue_909_basic_part_design_017_body.step +0 -0
  149. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/issue_915_case_study_2.step +0 -0
  150. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/nist_ctc_01_asme1_ap203.stp +0 -0
  151. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/nist_ctc_01_asme1_ap242.stp +0 -0
  152. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/nist_ctc_02_asme1_ap203.stp +0 -0
  153. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/nist_ctc_02_asme1_ap242.stp +0 -0
  154. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/nist_ctc_03_asme1_ap203.stp +0 -0
  155. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/nist_ctc_03_asme1_ap242.stp +0 -0
  156. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/nist_ctc_04_asme1_ap203.stp +0 -0
  157. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/nist_ctc_04_asme1_ap242.stp +0 -0
  158. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/nist_ctc_05_asme1_ap203.stp +0 -0
  159. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/nist_ctc_05_asme1_ap242.stp +0 -0
  160. {draftwright-0.4.14 → draftwright-0.4.16}/tests/fixtures/tuner_jig_blind_obround_pockets.step +0 -0
  161. {draftwright-0.4.14 → draftwright-0.4.16}/tests/refactor_golden/bracket_section.json +0 -0
  162. {draftwright-0.4.14 → draftwright-0.4.16}/tests/refactor_golden/centered_rebate.json +0 -0
  163. {draftwright-0.4.14 → draftwright-0.4.16}/tests/refactor_golden/chamfered.json +0 -0
  164. {draftwright-0.4.14 → draftwright-0.4.16}/tests/refactor_golden/filleted.json +0 -0
  165. {draftwright-0.4.14 → draftwright-0.4.16}/tests/refactor_golden/grid_plate.json +0 -0
  166. {draftwright-0.4.14 → draftwright-0.4.16}/tests/refactor_golden/grooved_shaft.json +0 -0
  167. {draftwright-0.4.14 → draftwright-0.4.16}/tests/refactor_golden/hex_bar.json +0 -0
  168. {draftwright-0.4.14 → draftwright-0.4.16}/tests/refactor_golden/holed_slot.json +0 -0
  169. {draftwright-0.4.14 → draftwright-0.4.16}/tests/refactor_golden/pocketed.json +0 -0
  170. {draftwright-0.4.14 → draftwright-0.4.16}/tests/refactor_golden/prismatic_ladder.json +0 -0
  171. {draftwright-0.4.14 → draftwright-0.4.16}/tests/refactor_golden/scattered_plate.json +0 -0
  172. {draftwright-0.4.14 → draftwright-0.4.16}/tests/refactor_golden/side_drilled.json +0 -0
  173. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_adr0018_arrangement_gate.py +0 -0
  174. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_adr0018_view_routing.py +0 -0
  175. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_agents_guide.py +0 -0
  176. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_api_docs.py +0 -0
  177. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_architecture_docs.py +0 -0
  178. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_balloon_ring_standoff.py +0 -0
  179. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_build_profile_harness.py +0 -0
  180. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_carve_free_position_callers.py +0 -0
  181. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_counting_calls.py +0 -0
  182. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_declare.py +0 -0
  183. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_dense_sheet_canary.py +0 -0
  184. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_deprecation_dates.py +0 -0
  185. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_detect_registry.py +0 -0
  186. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_dimension_role_vocabulary.py +0 -0
  187. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_drawing_encapsulation.py +0 -0
  188. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_e2e_slice.py +0 -0
  189. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_export_dxf_curves.py +0 -0
  190. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_export_dxf_zoom.py +0 -0
  191. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_external_recognition_boundary.py +0 -0
  192. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_fillets_adjacency.py +0 -0
  193. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_fits.py +0 -0
  194. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_flat_completeness.py +0 -0
  195. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_flat_stock_identity.py +0 -0
  196. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_flat_stock_opposition.py +0 -0
  197. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_gdt_placement.py +0 -0
  198. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_grid_lattice_convention.py +0 -0
  199. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_iso_nts_caption_placement.py +0 -0
  200. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1000_envelope_reuse.py +0 -0
  201. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1073_cross_body_patterns.py +0 -0
  202. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1082_polygonal_stock.py +0 -0
  203. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1086_declared_gears.py +0 -0
  204. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1116_ap242_hole_tolerance_lowering.py +0 -0
  205. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1142_hole_leader_labels.py +0 -0
  206. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1143_hole_completeness.py +0 -0
  207. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1144_transactional_hole_table.py +0 -0
  208. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1146_scale_completeness.py +0 -0
  209. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1147_legibility_pair_aggregation.py +0 -0
  210. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1153_contradictory_dimensions.py +0 -0
  211. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1154_one_owner_per_measurement.py +0 -0
  212. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1155_grm04_sheet_use.py +0 -0
  213. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1166_cross_pass_feature_leaders.py +0 -0
  214. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1176_quality_fidelity.py +0 -0
  215. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1177_angular_dimensions.py +0 -0
  216. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1187_unroutable_leaders.py +0 -0
  217. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1188_per_view_assignment.py +0 -0
  218. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1190_section_decision.py +0 -0
  219. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1196_deterministic_view_names.py +0 -0
  220. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1202_observed_drawing_consumer.py +0 -0
  221. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1204_multiscale_view_issues.py +0 -0
  222. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1209_linear_pmi_witnesses.py +0 -0
  223. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1215_envelope_tolerance.py +0 -0
  224. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1215_no_approved_tolerance_is_dropped.py +0 -0
  225. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1216_callout_reservation_measures_ink.py +0 -0
  226. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1216_pairwise_across_scale_groups.py +0 -0
  227. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1217_claimed_representations.py +0 -0
  228. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1219_location_claims_its_own_axis.py +0 -0
  229. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1229_ledger_member_keying.py +0 -0
  230. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1240_iso_above_strip_visibility.py +0 -0
  231. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1254_turned_chamfers.py +0 -0
  232. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1276_turned_leader_targets.py +0 -0
  233. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1281_turned_fillets.py +0 -0
  234. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1296_cylindrical_diameter_pmi.py +0 -0
  235. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1312_engine_costs.py +0 -0
  236. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1332_overlap_remedy.py +0 -0
  237. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1332_overlap_remedy_fast.py +0 -0
  238. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1336_grm03_ap242_pmi_fixture.py +0 -0
  239. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_1338_scale_before_page_escalation.py +0 -0
  240. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_367_leader_ink.py +0 -0
  241. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_676_polygonal_boss.py +0 -0
  242. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_676_polygonal_boss_declare.py +0 -0
  243. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_740_leader_assignment.py +0 -0
  244. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_798_floor_cardinality.py +0 -0
  245. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_798_material_field.py +0 -0
  246. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_885_prismatic_coverage.py +0 -0
  247. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_909_sloped_profile.py +0 -0
  248. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_915_dense_case.py +0 -0
  249. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_916_pocket_leader.py +0 -0
  250. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_917_open_channel.py +0 -0
  251. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_924_zero_length_shoulders.py +0 -0
  252. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_955_height_contingency.py +0 -0
  253. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_issue_958_cross_solid_recognition.py +0 -0
  254. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_layout.py +0 -0
  255. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_layout_cleanliness.py +0 -0
  256. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_layout_hypothesis.py +0 -0
  257. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_layout_property.py +0 -0
  258. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_leader_footprint.py +0 -0
  259. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_lint_box_cache.py +0 -0
  260. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_lint_ink_overlap.py +0 -0
  261. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_lint_reconciliation.py +0 -0
  262. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_lint_structural.py +0 -0
  263. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_linting.py +0 -0
  264. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_location_vocabulary.py +0 -0
  265. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_note_verb.py +0 -0
  266. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_object_aspects.py +0 -0
  267. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_occupancy_boxes.py +0 -0
  268. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_parameter_id.py +0 -0
  269. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_part_model.py +0 -0
  270. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_pitch_dim_footprint.py +0 -0
  271. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_pmi.py +0 -0
  272. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_pmi_datum_lowering.py +0 -0
  273. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_pmi_gtol_lowering.py +0 -0
  274. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_pmi_part21.py +0 -0
  275. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_pocket_pattern.py +0 -0
  276. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_pocket_pattern_recognition.py +0 -0
  277. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_principal_profile_classifier.py +0 -0
  278. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_private_test_imports.py +0 -0
  279. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_quality_components.py +0 -0
  280. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_recogniser_adoption.py +0 -0
  281. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_recogniser_contract.py +0 -0
  282. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_recognition.py +0 -0
  283. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_recognition_result.py +0 -0
  284. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_refactor_golden.py +0 -0
  285. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_repack_geometry_seam.py +0 -0
  286. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_score.py +0 -0
  287. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_shared_box_memo.py +0 -0
  288. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_sheet_emit.py +0 -0
  289. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_sheet_gdt.py +0 -0
  290. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_sheet_notes.py +0 -0
  291. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_sheet_section.py +0 -0
  292. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_sheet_tables.py +0 -0
  293. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_slanted_blind_step.py +0 -0
  294. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_slot_completeness.py +0 -0
  295. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_slot_pattern.py +0 -0
  296. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_slot_pattern_recognition.py +0 -0
  297. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_soft_deprecation.py +0 -0
  298. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_solve_trace.py +0 -0
  299. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_step_analysis_evaluation.py +0 -0
  300. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_suppression_ledger.py +0 -0
  301. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_suppression_marks.py +0 -0
  302. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_tolerances.py +0 -0
  303. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_turned_steps.py +0 -0
  304. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_witness_label_reconciliation.py +0 -0
  305. {draftwright-0.4.14 → draftwright-0.4.16}/tests/test_workflows.py +0 -0
@@ -4,6 +4,26 @@
4
4
 
5
5
  ### Added
6
6
 
7
+ - Referential and measured Sheet dimensions accept optional `view=` / `side=` placement
8
+ intent. Supported pairs survive IR/compiler and generated-script round trips, constrain
9
+ ordinary corridor candidates without exposing page coordinates, and fail clearly when a
10
+ renderer cannot honour the requested view/strip. Compound hole callouts can therefore be
11
+ authored to opposite sides of one end view while retaining normal solve and lint behavior
12
+ (#563).
13
+ - Feature-linked Sheet notes can explicitly satisfy one or more canonical requirement ids with
14
+ `.note(..., satisfies=(...))`. Placed structured notes retain each requirement's identity in
15
+ coverage reports and generated scripts without masquerading as dimensions or parsing prose;
16
+ removing the note restores the ordinary missing/suppressed diagnostics (#1351).
17
+ - `Sheet.from_part(...).take_over(...)` now adopts detected features in place while choosing
18
+ authored or automatic dimension, principal-view, and derived-view sources explicitly. This
19
+ preserves detected handles, supports automatic principal views with authored requirements,
20
+ replaces or suppresses inferred sections without duplicates, and round-trips the adopted view
21
+ request through generated Sheet code (#1350).
22
+ - Referential Sheet dimensions can preserve an explicit number of decimal places with
23
+ `sheet.dimension(feature, "parameter.id").format(decimals=n)`. The policy changes only
24
+ printed display text: numeric reconciliation, tolerances, suppression and dimension
25
+ provenance continue to use the feature parameter, and generated Sheet scripts round-trip the
26
+ policy (#1349).
7
27
  - New lint code `annotation_ink_overlap`: reports another annotation's line-work —
8
28
  a dimension line, an extension line or a leader shaft — drawn through a label's
9
29
  text, which `annotation_overlap` cannot see because it compares label boxes to
@@ -17,6 +37,11 @@
17
37
 
18
38
  ### Changed
19
39
 
40
+ - Declared Double-D bore, countersink, chamfer, fillet, and groove geometry reads now use the
41
+ stable `b123d_recognisers.inspection` namespace from the exact 0.4.5 wheel. A separate
42
+ Draftwright-owned format-1 contract fails closed on changes to the consumed signatures, result
43
+ schemas, units, bevel rejection reasons, or cylindrical surface layout (#1362).
44
+
20
45
  - `annotation_overlap` now reports the whole defect when the same pair also draws
21
46
  line-work through one of the two labels: the message names the crosser, the label
22
47
  crossed and the millimetres, and replaces the "use `label_offset_x`" remedy, which
@@ -40,6 +65,9 @@
40
65
 
41
66
  ### Fixed
42
67
 
68
+ - Non-1:1 orthographic and isometric projection now scales extracted solids about the world
69
+ origin, matching the view-coordinate mapper and solver zones. Solids carrying a non-zero
70
+ build123d `Location` no longer shift their silhouette away from dimensions and callouts.
43
71
  - Notes and generic table cells now render U+2300 `⌀` with the established supported drafting
44
72
  glyph instead of IBM Plex Mono's missing-glyph square. The substitution happens at the one
45
73
  shared measurement/render seam, so source rows remain unchanged and table sizing stays exact
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: draftwright
3
- Version: 0.4.14
3
+ Version: 0.4.16
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
7
7
  Project-URL: Issues, https://github.com/pzfreo/draftwright/issues
8
8
  Project-URL: Changelog, https://github.com/pzfreo/draftwright/blob/main/CHANGELOG.md
9
9
  Project-URL: Documentation, https://pzfreo.github.io/draftwright/
10
- Author-email: Paul Fremantle <pzfreo@gmail.com>
10
+ Author-email: Paul Fremantle <paul@fremantle.org>
11
11
  License: GNU AFFERO GENERAL PUBLIC LICENSE
12
12
  Version 3, 19 November 2007
13
13
 
@@ -684,7 +684,7 @@ Classifier: Programming Language :: Python :: 3.14
684
684
  Classifier: Topic :: Scientific/Engineering
685
685
  Requires-Python: <3.15,>=3.10
686
686
  Requires-Dist: antlr4-python3-runtime<4.10
687
- Requires-Dist: b123d-recognisers==0.3.1
687
+ Requires-Dist: b123d-recognisers==0.4.5
688
688
  Requires-Dist: build123d-drafting-helpers>=0.14.2
689
689
  Requires-Dist: build123d<0.11,>=0.9.0; python_full_version < '3.13'
690
690
  Requires-Dist: build123d<0.12,>=0.11; python_full_version >= '3.13'
@@ -823,6 +823,24 @@ a chainable handle (`sheet.hole(bore)`) that the aspect and `control(...)` verbs
823
823
  targets are placed automatically — the view and strip are derived from the referenced
824
824
  feature or face, with `view=`/`side=` overrides.
825
825
 
826
+ A feature-linked note can explicitly satisfy requirements that are legitimately communicated
827
+ as one compound shop instruction instead of separate dimensions. Use the canonical ids exposed
828
+ by the handle; free prose alone never changes coverage:
829
+
830
+ ```python
831
+ assert "counterbore.diameter" in hole.dimension_ids()
832
+ hole.note(
833
+ "PROFILED BORE: ⌀14 × 8 DEEP",
834
+ satisfies=("counterbore.diameter", "counterbore.depth"),
835
+ )
836
+ ```
837
+
838
+ Each id remains independent provenance in `drawing.registry.satisfaction_of(annotation_name)`;
839
+ the semantic family outcomes report `satisfied_by_structured_note`, while
840
+ `drawing.lint_summary()["quality"]["completeness"]` aggregates the count. The assertion counts
841
+ only if the normal placement solve places the note. Generated Sheet scripts preserve the
842
+ structured claim. Invalid, duplicate, ambiguous, or face-only claims fail at declaration time.
843
+
826
844
  ### From a part to an object-referenced script
827
845
 
828
846
  `draftwright part.step --script` writes an editable `Sheet` script. When you built the part
@@ -123,6 +123,24 @@ a chainable handle (`sheet.hole(bore)`) that the aspect and `control(...)` verbs
123
123
  targets are placed automatically — the view and strip are derived from the referenced
124
124
  feature or face, with `view=`/`side=` overrides.
125
125
 
126
+ A feature-linked note can explicitly satisfy requirements that are legitimately communicated
127
+ as one compound shop instruction instead of separate dimensions. Use the canonical ids exposed
128
+ by the handle; free prose alone never changes coverage:
129
+
130
+ ```python
131
+ assert "counterbore.diameter" in hole.dimension_ids()
132
+ hole.note(
133
+ "PROFILED BORE: ⌀14 × 8 DEEP",
134
+ satisfies=("counterbore.diameter", "counterbore.depth"),
135
+ )
136
+ ```
137
+
138
+ Each id remains independent provenance in `drawing.registry.satisfaction_of(annotation_name)`;
139
+ the semantic family outcomes report `satisfied_by_structured_note`, while
140
+ `drawing.lint_summary()["quality"]["completeness"]` aggregates the count. The assertion counts
141
+ only if the normal placement solve places the note. Generated Sheet scripts preserve the
142
+ structured claim. Invalid, duplicate, ambiguous, or face-only claims fail at declaration time.
143
+
126
144
  ### From a part to an object-referenced script
127
145
 
128
146
  `draftwright part.step --script` writes an editable `Sheet` script. When you built the part
@@ -11,6 +11,15 @@ completeness treatment, and documentation. A stage is either `supported`, `defer
11
11
  other CAD consumers apply different policy without making Draftwright policy part of the geometry
12
12
  library.
13
13
 
14
+ Declared-feature geometry reads use a different public boundary. The installed package's
15
+ inspection manifest format 1 is joined to the separate Draftwright-owned declaration in
16
+ `draftwright.inspection_contract`. That contract admits inspection API major 1 and exactly the
17
+ consumed `b123d_recognisers.inspection` symbol schemas: the five geometry readers, their public
18
+ result/error types, the cylindrical surface parameter layout, bevel rejection reasons, and the
19
+ semantic names and units of the Double-D tuple. It also checks the exact installed package
20
+ version. Recognition-family policy does not leak into this smaller measurement contract, and an
21
+ additive inspection symbol does not pretend to be a new recogniser family.
22
+
14
23
  ## What failures mean
15
24
 
16
25
  Validation fails closed and names the family and boundary whenever possible:
@@ -67,31 +76,23 @@ with no inferred gear feature added to fill the table.
67
76
 
68
77
  ## Passage compatibility boundary
69
78
 
70
- The installed `b123d-recognisers==0.3.1` release already contains the `passages` family introduced
71
- in 0.2.6. Draftwright declares its schema-v1 `Passage` output exhaustively but deliberately keeps
72
- the family `unsupported`, with the drafting decision tracked by issue #1245. This is a truthful
73
- consumer disposition: the geometry remains visible in the aggregate inventory, while Draftwright
74
- does not invent an IR feature, DSL declaration, generated code, drawing annotation, or completeness
75
- requirement for it.
76
-
77
- Draftwright's exact dependency pin must remain on a reviewed released version in the interval
78
- `>=0.2.6,<0.4.0` until the separately reviewed F4b transition is ready. The lower boundary ensures
79
- the installed manifest really contains `passages`; the upper boundary prevents accidental adoption
80
- of the planned 0.4 compatibility change.
79
+ The installed `b123d-recognisers==0.4.5` release contains the `passages` family introduced
80
+ in 0.2.6. Version 0.4.0 makes `SectionPassage` the authoritative physical output and retains
81
+ schema-v1 `Passage` as its compatibility projection. Draftwright declares all six public and nested
82
+ record schemas exhaustively but deliberately keeps the family `unsupported`, with the drafting
83
+ decision tracked by issue #1245. This is a truthful consumer disposition: both aggregate
84
+ inventories are visible and explicitly unscored, while Draftwright does not invent an IR feature,
85
+ DSL declaration, generated code, drawing annotation, or completeness requirement for them.
81
86
 
82
- The reviewed future 0.4 model is a distinct migration, not latent behavior in this declaration:
87
+ The 0.4 contract is explicit:
83
88
 
84
89
  - `SectionPassage` will be the authoritative physical output and aggregate census source;
85
90
  - legacy `Passage` values will be an accepted-only compatibility projection;
86
91
  - `recognise_passages(..., ledger=...)` will be a fail-loud unavailable compatibility operation;
87
92
  - the writer-free `recognise_passages` name will remain public but non-authoritative; and
88
- - rich split-junction passages can supersede a currently rendered Slot claim, moving ownership to
89
- the Passage family through `SLOT_SUPERSEDED_BY_PASSAGE`; and
90
- - the capability contract must represent those API and per-output roles exhaustively before the
91
- dependency pin can cross 0.4.
92
-
93
- That migration must update the package release, Draftwright adapter and declaration model,
94
- validator, tests, exact pin, and lockfile together. The current validator remains unchanged and
95
- fail closed; there is no forward declaration or stale-manifest exception. Crossing 0.4 also
96
- requires #1245 to decide what those newly Passage-owned occurrences draw, or an explicit reviewed
97
- decision to accept the resulting loss of the existing Slot callout.
93
+ - rich split-junction passages can supersede a Slot claim, moving physical ownership to the
94
+ unsupported Passage family through `SLOT_SUPERSEDED_BY_PASSAGE`.
95
+
96
+ The exact 0.4.5 pin, manifest-v2 validator and explicit unsupported inventories make that limitation
97
+ fail-visible rather than silently treating rich passages as supported. Issue #1245 still owns the
98
+ decision about what newly Passage-owned occurrences draw and how they participate in completeness.
@@ -0,0 +1,174 @@
1
+ # Sheet and fluent handles
2
+
3
+ `Sheet` is the declared authoring façade. Feature verbs return fluent handles; those handles
4
+ are documented here because they are part of normal use even though their Python names begin
5
+ with an underscore.
6
+
7
+ ## Sheet
8
+
9
+ ::: draftwright.sheet.Sheet
10
+ options:
11
+ filters: public
12
+
13
+ ### Authored views and layout
14
+
15
+ View declarations are semantic constraints. They name projections, relationships and whole-view
16
+ anchors; the layout solve owns the resulting page positions. Authored views must be paired with
17
+ authored dimensions so a deliberately omitted view cannot strand planner-selected annotations:
18
+
19
+ ```python
20
+ s = Sheet(part).authored_dimensions().authored_views()
21
+ front = s.view("front")
22
+ plan = s.view("plan").above(front, gap=4).align_x(front)
23
+ s.view("iso").scale(0.75)
24
+
25
+ section = s.section_view("A", through=hole)
26
+ detail = s.detail_view("B", around=hole).scale(2)
27
+ dwg = s.build()
28
+ ```
29
+
30
+ `view(...)`, `section_view(...)` and `detail_view(...)` define complete authored sets;
31
+ omission suppresses a view. `add_view(...)`, `add_section_view(...)` and
32
+ `add_detail_view(...)` augment an explicitly selected `auto_views()` source. `row(...)` and
33
+ `column(...)` are shorthand for whole-view relations. A principal-view handle's `pin((x, y))`
34
+ anchors its projection origin in page millimetres; it never positions an annotation.
35
+
36
+ Principal orthographic views share the sheet scale and reject `.scale(...)`. Detail and
37
+ isometric handles may carry an independent positive factor. Infeasible relations, scales and
38
+ pins raise with their declaration source; unsupported pins on derived or pictorial views are
39
+ refused at declaration and constraints are never silently relaxed. The immutable authored
40
+ snapshot is available as `sheet.view_constraints`, while `drawing.view_plan` is the distinct
41
+ resolved result.
42
+
43
+ ### Taking over a detected baseline
44
+
45
+ `Sheet.from_part(part)` retains the detector's feature set and starts with implicit automatic
46
+ dimensions. Use `take_over(...)` to adopt that same Sheet as an explicit editable request without
47
+ copying features into a second Sheet:
48
+
49
+ ```python
50
+ s = Sheet.from_part(part)
51
+ hole = s.of(next(feature for feature in s.features if feature.kind == "hole"))
52
+ s.take_over(
53
+ dimensions="authored",
54
+ principal_views="automatic",
55
+ derived_views="authored",
56
+ )
57
+ s.dimension(hole, "bore.diameter")
58
+ s.section_view("A", through=hole)
59
+ ```
60
+
61
+ The three sources are chosen atomically. In this example the principal planner keeps the
62
+ automatic front/plan/side/isometric baseline, while the authored derived set replaces inferred
63
+ sections. Leaving that authored set empty suppresses inferred derived views; selecting
64
+ `derived_views="automatic"` accepts them. Matching declarations may appear before or after
65
+ `take_over(...)` with the same result. Explicit source contradictions fail without partially
66
+ changing the Sheet, and automatic dimensions remain incompatible with authored views under ADR
67
+ 0018.
68
+
69
+ To emit editable Python from an adopted Sheet, pass both request halves to the script emitter;
70
+ the model carries the feature/dimension semantics and `view_constraints` carries the independent
71
+ view sources and semantic targets:
72
+
73
+ ```python
74
+ from draftwright.sheet_emit import emit_sheet_script
75
+
76
+ source = emit_sheet_script(
77
+ s.model(),
78
+ "part = make_part()",
79
+ "drawing",
80
+ title="BRACKET",
81
+ number="DWG-001",
82
+ view_constraints=s.view_constraints,
83
+ )
84
+ ```
85
+
86
+ ### Refining a staged hole into manufacturing authority
87
+
88
+ A detected hole remains a live feature handle after takeover. Enrich that handle instead of
89
+ declaring a second, unrelated annotation. Counterbore/spotface tools can supply their geometry;
90
+ thread and fit are authored manufacturing intent:
91
+
92
+ ```python
93
+ s = Sheet.from_part(part, page="A2", scale=1)
94
+ stack = s.of(next(feature for feature in s.features if feature.kind == "hole"))
95
+ s.take_over(
96
+ dimensions="authored",
97
+ principal_views="automatic",
98
+ derived_views="authored",
99
+ )
100
+
101
+ stack.cbore(collar_tool) # diameter and depth re-read from the tool
102
+ stack.thread("M6x1", depth=12).fit("H8")
103
+ for parameter_id in stack.dimension_ids():
104
+ intent = s.dimension(stack, parameter_id)
105
+ if parameter_id == "counterbore.diameter":
106
+ intent.format(decimals=2) # 6.35 remains 6.35, still feature-linked
107
+
108
+ s.section_view("A", through=stack) # replaces inferred derived views
109
+ drawing = s.build()
110
+ assert not [issue for issue in drawing.lint() if issue.severity != "info"]
111
+ drawing.export("quote/grm01", formats=("pdf", "svg", "dxf"))
112
+ ```
113
+
114
+ An explicit tap depth is a real `thread.depth` parameter: it appears in
115
+ `dimension_ids()`, participates in authored-set suppression and placement, and round-trips
116
+ through generated Sheet code. `fit("H8")` applies only to `bore.diameter`; it does not leak
117
+ onto a counterbore diameter. A plain `thread("M6x1")` remains valid when no independent depth is
118
+ specified.
119
+
120
+ This surface models the common bore + recess + tap-depth stack in one solver-participating
121
+ callout. More general ordered operation stacks remain tracked by issue #1360. Until a physical
122
+ requirement has a structured parameter, use a feature-linked `note(..., satisfies=(...))` only
123
+ for parameter ids the handle actually exposes; free prose does not satisfy coverage.
124
+
125
+ ## View handle
126
+
127
+ ::: draftwright.sheet._View
128
+ options:
129
+ filters: public
130
+
131
+ ## Dimension intent handle
132
+
133
+ `dimension(feature, parameter_id)` and `add_dimension(feature, parameter_id)` return a
134
+ referential `DimensionIntent`. The handle never carries a replacement nominal and never chooses
135
+ page coordinates. Use `format(decimals=n)` to preserve between 0 and 15 decimal places in the
136
+ printed nominal while reconciliation, tolerance, suppression and provenance continue to read the
137
+ numeric parameter from the feature. Optional `view="front|plan|side"` and
138
+ `side="above|below|left|right"` arguments select a supported semantic corridor when authored
139
+ routing must override the derived default; the normal placement solve still chooses coordinates
140
+ and reports capacity/crossing failures. Trailing zeroes are intentional manufacturing display text:
141
+
142
+ ```python
143
+ sheet.authored_dimensions()
144
+ sheet.dimension(envelope, "width.length").format(decimals=2) # 13.5 prints as 13.50
145
+ sheet.dimension(tapped_hole, "bore.diameter", view="plan", side="left")
146
+ ```
147
+
148
+ ::: draftwright.sheet.DimensionIntent
149
+ options:
150
+ filters: public
151
+
152
+ ## Hole handle
153
+
154
+ ::: draftwright.sheet._Hole
155
+ options:
156
+ filters: public
157
+
158
+ ## Diameter and step handle
159
+
160
+ ::: draftwright.sheet._Dim
161
+ options:
162
+ filters: public
163
+
164
+ ## Multi-parameter handle
165
+
166
+ ::: draftwright.sheet._Params
167
+ options:
168
+ filters: public
169
+
170
+ ## GD&T control builder
171
+
172
+ ::: draftwright.sheet._Control
173
+ options:
174
+ filters: public
@@ -4,11 +4,11 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "draftwright"
7
- version = "0.4.14"
7
+ version = "0.4.16"
8
8
  description = "Automated technical-drawing generation for build123d"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
11
- authors = [{ name = "Paul Fremantle", email = "pzfreo@gmail.com" }]
11
+ authors = [{ name = "Paul Fremantle", email = "paul@fremantle.org" }]
12
12
  requires-python = ">=3.10,<3.15"
13
13
  dependencies = [
14
14
  # build123d ≤0.10 pulls cadquery-ocp (VTK), whose VTK caps at Python 3.12; 0.11 switched to
@@ -18,7 +18,7 @@ dependencies = [
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
20
  "build123d-drafting-helpers>=0.14.2",
21
- "b123d-recognisers==0.3.1",
21
+ "b123d-recognisers==0.4.5",
22
22
  "numpy>=1.24",
23
23
  # PNG raster export (SVG→PDF→PNG). Both permissively licensed (Pillow HPND; pypdfium2
24
24
  # Apache-2.0 wrapping Google PDFium BSD-3) and ship pre-built wheels with NO native system
@@ -29,7 +29,18 @@ if TYPE_CHECKING:
29
29
 
30
30
  from draftwright.compose import StripDepths
31
31
 
32
- from build123d import Align, BoundBox, Compound, Edge, Location, Mode, Shape, Text, Vector
32
+ from build123d import (
33
+ Align,
34
+ BoundBox,
35
+ Compound,
36
+ Edge,
37
+ FontStyle,
38
+ Location,
39
+ Mode,
40
+ Shape,
41
+ Text,
42
+ Vector,
43
+ )
33
44
  from build123d_drafting.helpers import (
34
45
  Dimension,
35
46
  TitleBlock,
@@ -86,7 +97,16 @@ def _decode_hole_location_fact(fact):
86
97
  return feature, parameter, point
87
98
 
88
99
 
89
- def place_annotation(registry, items, obj, name=None, view=None, feature=None, measurement=None):
100
+ def place_annotation(
101
+ registry,
102
+ items,
103
+ obj,
104
+ name=None,
105
+ view=None,
106
+ feature=None,
107
+ measurement=None,
108
+ satisfaction=None,
109
+ ):
90
110
  """The annotation-placement primitive (#817): register *obj* under *name* — replacing any
91
111
  prior object of that name (dropped from the render list *items*) so a name maps to one
92
112
  object — append it to *items*, and record its owning *view* + source *feature* in *registry*.
@@ -94,13 +114,15 @@ def place_annotation(registry, items, obj, name=None, view=None, feature=None, m
94
114
  annotations without reaching into the ``Drawing``. Returns *obj*.
95
115
 
96
116
  *measurement* is the `DimensionId` *obj* draws, where the caller holds one (#1002) —
97
- one axis finer than *feature*, since a feature has several measurements."""
117
+ one axis finer than *feature*, since a feature has several measurements. *satisfaction*
118
+ is the separate set of requirements a structured note explicitly satisfies (#1351); it
119
+ never claims that the note drew a measurement."""
98
120
  displaced = registry.named(name) if name is not None else None
99
121
  if displaced is not None:
100
122
  items.remove(displaced)
101
123
  annotate(obj, name)
102
124
  items.append(obj)
103
- registry.add(obj, name, view, feature, measurement)
125
+ registry.add(obj, name, view, feature, measurement, satisfaction)
104
126
  return obj
105
127
 
106
128
 
@@ -362,6 +384,7 @@ def _text_size(
362
384
  font_size: float,
363
385
  font_path: str | None = PLEX_MONO,
364
386
  font: str = "Arial",
387
+ font_style: FontStyle = FontStyle.REGULAR,
365
388
  ) -> tuple[float, float]:
366
389
  """Measured rendered (width, height) (page-mm) of *text* at *font_size*.
367
390
 
@@ -383,12 +406,43 @@ def _text_size(
383
406
  font_size=font_size,
384
407
  font=font,
385
408
  font_path=font_path,
409
+ font_style=font_style,
386
410
  align=(Align.CENTER, Align.CENTER),
387
411
  mode=Mode.PRIVATE,
388
412
  ).bounding_box()
389
413
  return (bb.size.X, bb.size.Y)
390
414
 
391
415
 
416
+ @functools.lru_cache(maxsize=32)
417
+ def _text_line_spacing_em(
418
+ font_size: float,
419
+ font_path: str | None = PLEX_MONO,
420
+ font: str = "Arial",
421
+ ) -> float:
422
+ """Return OCCT's actual newline advance for a regular face, in em units.
423
+
424
+ The advance is face-dependent (Plex Mono is 1.3 em; named Arial on macOS is
425
+ about 1.15 em). Measure the same repeated glyph twice through build123d so
426
+ the invisible PDF layer follows the renderer instead of baking either value.
427
+ """
428
+ probe = Text(
429
+ txt="A\nA",
430
+ font_size=font_size,
431
+ font=font,
432
+ font_path=font_path,
433
+ font_style=FontStyle.REGULAR,
434
+ align=(Align.CENTER, Align.CENTER),
435
+ mode=Mode.PRIVATE,
436
+ )
437
+ centres = sorted(
438
+ ((face.bounding_box().min.Y + face.bounding_box().max.Y) / 2.0 for face in probe.faces()),
439
+ reverse=True,
440
+ )
441
+ if len(centres) < 2: # pragma: no cover - defensive for a pathological font
442
+ return 1.3
443
+ return abs(centres[0] - centres[-1]) / font_size
444
+
445
+
392
446
  def _text_width(text: str, font_size: float, font_path: str = PLEX_MONO) -> float:
393
447
  """Measured rendered width (page-mm) of *text* — see :func:`_text_size`."""
394
448
  return _text_size(text, font_size, font_path)[0]
@@ -1207,16 +1261,25 @@ def _make_title_block(dwg, a: Analysis):
1207
1261
  drawn-by cell bbox (for the hyperlink rect). Shared by :func:`_add_title_block` (which adds
1208
1262
  it, last) and :func:`_title_block_box` (which measures its footprint for GD&T avoidance, #481)
1209
1263
  so the two never drift."""
1264
+ title = _font_safe_text(a.title)
1265
+ number = _font_safe_text(a.number)
1266
+ tolerance = _font_safe_text(a.tolerance)
1267
+ designed_by = _font_safe_text(_attribution_author(a.drawn_by))
1268
+ material = _font_safe_text(a.material)
1269
+ date = _font_safe_text(a.date)
1270
+ revision = _font_safe_text(a.revision)
1271
+ legal_owner = _font_safe_text(a.company)
1272
+ scale = format_drawing_scale(a.SCALE)
1210
1273
  tb = TitleBlock(
1211
- _font_safe_text(a.title),
1212
- _font_safe_text(a.number),
1213
- scale=format_drawing_scale(a.SCALE),
1214
- general_tolerance=_font_safe_text(a.tolerance),
1215
- designed_by=_font_safe_text(_attribution_author(a.drawn_by)),
1216
- material=_font_safe_text(a.material),
1217
- date=_font_safe_text(a.date),
1218
- revision=_font_safe_text(a.revision),
1219
- legal_owner=_font_safe_text(a.company),
1274
+ title,
1275
+ number,
1276
+ scale=scale,
1277
+ general_tolerance=tolerance,
1278
+ designed_by=designed_by,
1279
+ material=material,
1280
+ date=date,
1281
+ revision=revision,
1282
+ legal_owner=legal_owner,
1220
1283
  width=a.TB_W,
1221
1284
  # Title block renders in condensed sans (the tight ISO 7200 cells), a
1222
1285
  # different face from the monospace dimensions — so it carries its own
@@ -1231,7 +1294,37 @@ def _make_title_block(dwg, a: Analysis):
1231
1294
  # than hardcoded column fractions, so the hyperlink rect tracks any upstream
1232
1295
  # TitleBlock layout change. Build-frame bbox; translated to page space below.
1233
1296
  cell = tb.drawn_by_cell_bbox()
1234
- tb = tb.locate(Location((a.PAGE_W - a.TB_W - _TB_CLEAR, _TB_CLEAR, 0)))
1297
+ bx, by = a.PAGE_W - a.TB_W - _TB_CLEAR, _TB_CLEAR
1298
+ tb = tb.locate(Location((bx, by, 0)))
1299
+
1300
+ # Retain authoritative title-block values at their public cell centres for the PDF semantic
1301
+ # text layer. The visible block stays path-rendered; these specs merely let export embed the
1302
+ # same bundled condensed face as invisible selectable text without parsing SVG geometry.
1303
+ fields = (
1304
+ ("title", title),
1305
+ ("drawing_number", number),
1306
+ ("scale", scale),
1307
+ ("material", material),
1308
+ ("revision", revision or date),
1309
+ ("general_tolerance", tolerance),
1310
+ ("designed_by", designed_by),
1311
+ ("legal_owner", legal_owner),
1312
+ )
1313
+ specs = []
1314
+ for field, value in fields:
1315
+ if not value:
1316
+ continue
1317
+ box = tb.cell_bbox(field)
1318
+ specs.append(
1319
+ (
1320
+ value,
1321
+ bx + (box["min_x"] + box["max_x"]) / 2.0,
1322
+ by + (box["min_y"] + box["max_y"]) / 2.0,
1323
+ dwg.draft.font_size,
1324
+ PLEX_SANS_CONDENSED,
1325
+ )
1326
+ )
1327
+ tb.pdf_text_specs = tuple(specs)
1235
1328
  return tb, cell
1236
1329
 
1237
1330
 
@@ -14,9 +14,10 @@ from __future__ import annotations
14
14
  import logging
15
15
  import math
16
16
  from dataclasses import dataclass
17
+ from inspect import signature
17
18
 
18
19
  from b123d_recognisers import full_cylinders
19
- from build123d import Compound
20
+ from build123d import Compound, Shape
20
21
 
21
22
  _log = logging.getLogger(__name__)
22
23
 
@@ -190,6 +191,23 @@ def _solids_body(part, src: str = "part"):
190
191
  return body
191
192
 
192
193
 
194
+ _SCALE_SUPPORTS_ABOUT = "about" in signature(Shape.scale).parameters
195
+
196
+
197
+ def _scale_world(shape, factor: float):
198
+ """Scale a build123d shape about the world origin on every supported version.
199
+
200
+ build123d 0.9/0.10's factor-only ``Shape.scale`` uses the world origin. Version 0.11 added
201
+ ``about=`` and changed the default to ``shape.location.position``. Draftwright supports both
202
+ dependency lines, so select the signature once and state the same world-origin transform on
203
+ either API without converting analytic geometry through a general affine transform.
204
+ """
205
+
206
+ if _SCALE_SUPPORTS_ABOUT:
207
+ return shape.scale(factor, about=(0, 0, 0))
208
+ return shape.scale(factor)
209
+
210
+
193
211
  def _axis_letter(obj) -> str:
194
212
  """Letter (``"x"``/``"y"``/``"z"``) of ``obj.axis``'s dominant component.
195
213
 
@@ -337,11 +355,18 @@ def _axis_direction_is_aligned(axis: str, direction, *, tol: float = 1e-3) -> bo
337
355
  )
338
356
 
339
357
 
340
- def _fmt(v: float) -> str:
358
+ def _fmt(v: float, decimals: int | None = None) -> str:
341
359
  """Format a float as integer string if whole, otherwise 1 dp. The one number
342
360
  formatter the IR (:mod:`draftwright.model.ir`) and the drawing layers
343
361
  (:mod:`draftwright._core`) share (#700 — the two copies had already begun to
344
- drift on ``-0``)."""
362
+ drift on ``-0``).
363
+
364
+ ``decimals`` is an explicit referential-dimension display policy (#1349). It preserves
365
+ trailing zeroes by design; ``None`` retains the established automatic formatting.
366
+ """
367
+ if decimals is not None:
368
+ normalized = 0.0 if round(float(v), decimals) == 0 else float(v)
369
+ return f"{normalized:.{decimals}f}"
345
370
  r = round(v)
346
371
  return str(r) if abs(v - r) < 1e-6 else f"{v:.1f}"
347
372