draftwright 0.4.2__tar.gz → 0.4.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {draftwright-0.4.2 → draftwright-0.4.4}/CHANGELOG.md +136 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/PKG-INFO +42 -6
- {draftwright-0.4.2 → draftwright-0.4.4}/README.md +40 -4
- {draftwright-0.4.2 → draftwright-0.4.4}/docs/adr/README.md +3 -2
- {draftwright-0.4.2 → draftwright-0.4.4}/pyproject.toml +1 -1
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/__init__.py +6 -2
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/_core.py +17 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/_geometry.py +99 -9
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/_warnings.py +8 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/analysis.py +84 -42
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/annotations/_common.py +445 -6
- draftwright-0.4.4/src/draftwright/annotations/balloons.py +1128 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/annotations/from_model.py +181 -41
- draftwright-0.4.4/src/draftwright/annotations/gears.py +72 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/annotations/holes.py +220 -89
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/annotations/orchestrator.py +443 -67
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/audit.py +16 -17
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/builder.py +404 -6
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/cli.py +20 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/compose.py +38 -8
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/drawing.py +270 -42
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/layout.py +294 -27
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/linting/__init__.py +6 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/linting/coverage.py +23 -11
- draftwright-0.4.4/src/draftwright/linting/gear_coverage.py +185 -0
- draftwright-0.4.4/src/draftwright/linting/hole_coverage.py +1008 -0
- draftwright-0.4.4/src/draftwright/linting/issues.py +89 -0
- draftwright-0.4.4/src/draftwright/linting/polygonal_stock_coverage.py +114 -0
- draftwright-0.4.4/src/draftwright/linting/quality.py +352 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/linting/slot_coverage.py +29 -1
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/linting/structural.py +120 -40
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/model/__init__.py +8 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/model/callout.py +46 -10
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/model/compiled.py +37 -11
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/model/declare.py +59 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/model/detect.py +39 -18
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/model/ir.py +173 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/model/planner.py +22 -40
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/recognition/__init__.py +21 -2
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/recognition/_features.py +2 -17
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/recognition/countersinks.py +47 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/recognition/polygonal_bosses.py +80 -10
- draftwright-0.4.4/src/draftwright/recognition/repeating_profiles.py +393 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/recognition/result.py +13 -1
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/recognition/slots.py +48 -2
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/registry.py +16 -2
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/sheet.py +65 -26
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/sheet_emit.py +64 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/refactor_golden/bracket_section.json +2 -2
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/refactor_golden/flange_dense.json +1 -1
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/refactor_golden/grid_plate.json +9 -3
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/refactor_golden/holed_slot.json +1 -1
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/refactor_golden/pocketed.json +1 -1
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/refactor_golden/scattered_plate.json +4 -4
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/refactor_golden/side_drilled.json +1 -1
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_add_dimension.py +21 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_audit_differential.py +20 -32
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_compiled_plan_boundary.py +1 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_declare.py +8 -8
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_e2e_standards.py +48 -59
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_flat_completeness.py +33 -1
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_gdt_placement.py +7 -1
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_issue_1058_wheel_profile.py +167 -136
- draftwright-0.4.4/tests/test_issue_1073_cross_body_patterns.py +95 -0
- draftwright-0.4.4/tests/test_issue_1082_polygonal_stock.py +232 -0
- draftwright-0.4.4/tests/test_issue_1086_declared_gears.py +291 -0
- draftwright-0.4.4/tests/test_issue_1142_hole_leader_labels.py +247 -0
- draftwright-0.4.4/tests/test_issue_1143_hole_completeness.py +2716 -0
- draftwright-0.4.4/tests/test_issue_1144_transactional_hole_table.py +1774 -0
- draftwright-0.4.4/tests/test_issue_1146_scale_completeness.py +539 -0
- draftwright-0.4.4/tests/test_issue_1147_legibility_pair_aggregation.py +417 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_issue_915_dense_case.py +7 -1
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_issue_955_height_contingency.py +7 -1
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_label_provenance.py +0 -1
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_layout.py +242 -0
- draftwright-0.4.4/tests/test_leader_footprint.py +170 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_make_drawing.py +210 -27
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_parameter_id.py +39 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_pmi.py +1 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_private_test_attr_reads.py +1 -0
- draftwright-0.4.4/tests/test_quality_components.py +170 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_recogniser_contract.py +12 -1
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_recognition_manifest.py +16 -1
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_recognition_result.py +8 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_registry.py +43 -2
- draftwright-0.4.4/tests/test_repack_geometry_seam.py +63 -0
- draftwright-0.4.4/tests/test_shared_box_memo.py +101 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_sheet_emit.py +42 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_sheet_identity_invariant.py +2 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_sheet_tables.py +114 -2
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_slot_completeness.py +93 -3
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_strip_layout.py +13 -1
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_suppression_ledger.py +12 -20
- draftwright-0.4.4/tests/test_workflows.py +108 -0
- draftwright-0.4.2/src/draftwright/annotations/balloons.py +0 -342
- draftwright-0.4.2/src/draftwright/linting/issues.py +0 -29
- draftwright-0.4.2/tests/test_workflows.py +0 -70
- {draftwright-0.4.2 → draftwright-0.4.4}/.gitignore +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/LICENSE +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/skills/SKILL.md +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/_pmi_part21.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/annotate.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/annotations/__init__.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/annotations/sections.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/export.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/fits.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/fonts/IBMPlexMono-Regular.ttf +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/fonts/IBMPlexSansCondensed-Regular.ttf +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/fonts/LICENSE-IBMPlexMono-OFL.txt +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/fonts/LICENSE-IBMPlexSansCondensed-OFL.txt +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/fonts/__init__.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/intents.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/linting/channel_coverage.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/linting/flat_coverage.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/linting/pmi_coverage.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/linting/profiled_bore_coverage.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/linting/suggest.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/make_drawing.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/pmi.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/projection.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/py.typed +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/recognition/_record.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/recognition/chamfers.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/recognition/fillets.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/recognition/flats.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/recognition/grooves.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/recognition/levels.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/recognition/pads.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/recognition/plates.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/recognition/profiled_bores.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/recognition/turned.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/repair.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/src/draftwright/score.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/_kernel.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/_layout_sig.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/conftest.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/fixtures/grm03_thumbwheel_drive_screw.step +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/fixtures/if_step_flat_across_cylinder.step +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/fixtures/issue_1058_wheel_rh.step +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/fixtures/issue_909_basic_part_design_017_body.step +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/fixtures/issue_915_case_study_2.step +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/fixtures/nist_ctc_01_asme1_ap203.stp +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/fixtures/nist_ctc_01_asme1_ap242.stp +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/fixtures/nist_ctc_02_asme1_ap203.stp +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/fixtures/nist_ctc_02_asme1_ap242.stp +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/fixtures/nist_ctc_03_asme1_ap203.stp +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/fixtures/nist_ctc_03_asme1_ap242.stp +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/fixtures/nist_ctc_04_asme1_ap203.stp +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/fixtures/nist_ctc_04_asme1_ap242.stp +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/fixtures/nist_ctc_05_asme1_ap203.stp +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/fixtures/nist_ctc_05_asme1_ap242.stp +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/fixtures/tuner_jig_blind_obround_pockets.step +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/refactor_golden/centered_rebate.json +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/refactor_golden/chamfered.json +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/refactor_golden/filleted.json +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/refactor_golden/grooved_shaft.json +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/refactor_golden/hex_bar.json +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/refactor_golden/prismatic_ladder.json +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/refactor_golden/turned_stepped.json +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_agents_guide.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_api_docs.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_architecture_docs.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_balloon_ring_standoff.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_carve_free_position_callers.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_counting_calls.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_declared_recognition_gate.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_dense_sheet_canary.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_deprecation_dates.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_detect_once.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_detect_registry.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_dimension_role_vocabulary.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_drawing_encapsulation.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_e2e_slice.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_export_dxf_curves.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_export_dxf_zoom.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_fillets_adjacency.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_fits.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_flat_stock_identity.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_flat_stock_opposition.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_grid_lattice_convention.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_import_boundaries.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_issue_1000_envelope_reuse.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_issue_676_polygonal_boss.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_issue_676_polygonal_boss_declare.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_issue_885_prismatic_coverage.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_issue_909_sloped_profile.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_issue_916_pocket_leader.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_issue_917_open_channel.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_issue_958_cross_solid_recognition.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_layout_cleanliness.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_layout_hypothesis.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_layout_property.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_lint_box_cache.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_lint_reconciliation.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_lint_structural.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_linting.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_location_vocabulary.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_note_verb.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_object_aspects.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_occupancy_boxes.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_part_model.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_pitch_dim_footprint.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_pmi_datum_lowering.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_pmi_gtol_lowering.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_pmi_part21.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_pocket_pattern.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_pocket_pattern_recognition.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_principal_profile_classifier.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_private_test_imports.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_recognition.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_refactor_golden.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_render_seam.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_score.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_script_detail_parity.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_sheet_gdt.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_sheet_notes.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_sheet_of.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_sheet_section.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_slanted_blind_step.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_slot_pattern.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_slot_pattern_recognition.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_soft_deprecation.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_solve_trace.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_suppression_marks.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_tolerances.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_turned_steps.py +0 -0
- {draftwright-0.4.2 → draftwright-0.4.4}/tests/test_witness_label_reconciliation.py +0 -0
|
@@ -2,6 +2,142 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## v0.4.4 — 2026-08-15
|
|
6
|
+
|
|
7
|
+
**A semantic-completeness and fail-closed layout patch release.** Hole requirements and drawing
|
|
8
|
+
quality are now machine-readable, hole-table replacement is transactional, explicit scales
|
|
9
|
+
preserve required output, notes tables use all viable sheet space, and gear declarations,
|
|
10
|
+
recognition evidence, and callout metadata improve diagnostic trust.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **External spur gears can be declared through the `Sheet` façade** (#1086). The declaration
|
|
15
|
+
records the correlated ISO 1328-1 manufacturing requirements, renders a structured gear-data
|
|
16
|
+
table, and survives script emission, declaration, export, and lint without inferring gear data
|
|
17
|
+
that the caller did not supply.
|
|
18
|
+
|
|
19
|
+
- **Recognition carries serialisable repeating radial-profile evidence** (#1087). Exact
|
|
20
|
+
rotational whole-wire proof can reconcile a declared gear with its physical profile while
|
|
21
|
+
preserving ambiguity across separate bodies and without inventing module, pressure angle, or
|
|
22
|
+
other manufacturing intent.
|
|
23
|
+
|
|
24
|
+
- **Explicit drawing scales now protect required annotation completeness** (#1146).
|
|
25
|
+
`build_drawing(...)`, `make_drawing(...)`, `Sheet(...)`, and the CLI accept
|
|
26
|
+
`scale_policy="fallback"|"strict"|"permissive"`. The safe default retries preferred ISO
|
|
27
|
+
5455 reductions and returns the largest complete scale; strict mode raises with structured
|
|
28
|
+
requirement blockers, while permissive mode is an explicit warned opt-in to the historical
|
|
29
|
+
best-effort result. Every returned `Drawing.scale_decision` reports the requested and
|
|
30
|
+
effective scales, status, attempted scales, and any semantic placement blockers.
|
|
31
|
+
|
|
32
|
+
- **Hole and hole-pattern requirements now participate in semantic completeness accounting**
|
|
33
|
+
(#1143). `lint_summary()["quality"]["completeness"]` reconciles recognition-owned bore,
|
|
34
|
+
depth/through, grouping, pattern, and location requirements to placed, suppressed, dropped,
|
|
35
|
+
missing, or explicitly unverifiable outcomes. Ambiguous declared/automatic correspondence
|
|
36
|
+
fails closed without shrinking the physical denominator. Z-normal hole locations retain ADR
|
|
37
|
+
0016's feature-level `location.location` addressability while structured physical evidence
|
|
38
|
+
distinguishes the independently required X and Y ordinates for critique.
|
|
39
|
+
|
|
40
|
+
- **`lint_summary()` exposes drawing-quality evidence as separate completeness, restraint,
|
|
41
|
+
and legibility components** (#1127). The legacy `score` remains byte-compatible and is also
|
|
42
|
+
returned as the honestly named `diagnostic_score`; no composite quality verdict is invented.
|
|
43
|
+
Completeness scores recognition-owned requirements in the families with semantic outcome
|
|
44
|
+
ledgers and states that conditional scope explicitly; recognized families not yet scored are
|
|
45
|
+
listed separately. Its scalar is `audited_score`, not `score`, because a feature recognition
|
|
46
|
+
missed never became a requirement — so a part with a recogniser gap can reach 1.0, and the
|
|
47
|
+
qualifier belongs where it survives being quoted. The block also lists what its denominator
|
|
48
|
+
`excludes` and counts `unrecognised_geometry_reports` (a floor on that gap, not a measure).
|
|
49
|
+
It is not a completion gate. Legibility includes only layout and placement diagnostics, and
|
|
50
|
+
scores information-severity ones — "place what fits" drops, a leader crossing a silhouette —
|
|
51
|
+
against the warning floor, so it cannot read 1.0 while itemising output it calls unreadable.
|
|
52
|
+
A drop is identified by its `*_dropped` code suffix unless its producer records an explicit
|
|
53
|
+
`outcome_stage`, so a drop code added later counts without being registered anywhere.
|
|
54
|
+
Restraint fails closed as unavailable until measurement provenance can classify every
|
|
55
|
+
annotation.
|
|
56
|
+
|
|
57
|
+
### Fixed
|
|
58
|
+
|
|
59
|
+
- **Generic notes/data tables use every viable free sheet region and explain failed placement**
|
|
60
|
+
(#1145). A drawn sheet frame or zone ruler now defines the usable inset instead of becoming a
|
|
61
|
+
page-sized solid obstacle, so a measured four-row notes block can use clear lower-left A4
|
|
62
|
+
space. Tables retain the drafting preset's external text clearance from named and anonymous
|
|
63
|
+
existing annotations. Failed placement diagnostics report the measured page-space footprint,
|
|
64
|
+
attempted candidate regions, and the named obstacles or clearance bands that blocked the
|
|
65
|
+
nearest candidates without materialising the candidate Cartesian product.
|
|
66
|
+
|
|
67
|
+
- **Automatic hole-table escalation is transactional per semantic requirement** (#1144). The
|
|
68
|
+
engine suppresses replaceable callouts and location dimensions only after the table fits and
|
|
69
|
+
every visible row has its required feature-owned balloon. A failed or partial balloon attempt
|
|
70
|
+
rolls the shared table back as a unit and restores the exact annotations, ordering, registry
|
|
71
|
+
identities, coverage, and issues. Precise ring, glyph, and leader-segment tests keep placement
|
|
72
|
+
clear of retained callout labels, leader shafts, and existing public-balloon components without
|
|
73
|
+
changing the existing additive `add_hole_table()`/`add_balloons()` behavior; malformed component
|
|
74
|
+
metadata—including Boolean cardinalities or coordinate payloads—falls back conservatively or fails the
|
|
75
|
+
optional replacement closed. New balloon shafts must also remain mutually crossing-free.
|
|
76
|
+
Compound, thread,
|
|
77
|
+
profile, pattern, tolerance, and fit facts the table cannot state remain active, while
|
|
78
|
+
independently replaceable X/Y locations may still move to the table. Guarded interval solving
|
|
79
|
+
has deterministic pre-lane, pre-carve, and work/state budgets; an adversarially fragmented label
|
|
80
|
+
inventory or band fails the replacement closed before excessive work or allocation. Required row-key
|
|
81
|
+
balloons take priority over optional non-certifying pattern markers, so an independently complete
|
|
82
|
+
table cannot be displaced by an auxiliary marker.
|
|
83
|
+
Hole outcomes identify the winning representation per requirement;
|
|
84
|
+
mixed table/feature evidence deliberately claims no single winner. Exceptions restore those
|
|
85
|
+
semantic markers, and automatic escalation fails closed rather than overwrite an existing
|
|
86
|
+
annotation that owns a reserved table or balloon name.
|
|
87
|
+
|
|
88
|
+
- **Generated hole-callout leaders expose their exact rendered semantic text through the public
|
|
89
|
+
`Leader.label` field** (#1142). Geometry and placement are unchanged, and structured coverage
|
|
90
|
+
remains authoritative instead of requiring downstream tools to parse display text.
|
|
91
|
+
|
|
92
|
+
- **Leader placement probes use analytic footprint arithmetic instead of constructing temporary
|
|
93
|
+
OCCT geometry** (#1138). Representative probe-heavy layouts are substantially faster while
|
|
94
|
+
preserving custom-style containment and placement behavior.
|
|
95
|
+
|
|
96
|
+
### Changed
|
|
97
|
+
|
|
98
|
+
- **Pairwise lint detail no longer multiplies the legibility score penalty** (#1147). Raw
|
|
99
|
+
severity and code counts retain their existing finding-level semantics, and every offending
|
|
100
|
+
pair remains in `issues`. The legibility component now also reports its primary issue counts
|
|
101
|
+
and scores one producer-identified annotation/failure mechanism once, with `affected_pairs`
|
|
102
|
+
exposing how many raw pair findings were aggregated. The legacy top-level `score` and
|
|
103
|
+
`diagnostic_score` remain unchanged.
|
|
104
|
+
|
|
105
|
+
- **Pull-request validation now reports a stable aggregate `ci-ok` check and runs the supported
|
|
106
|
+
Python matrix on Linux** (#1134/#1136). Weekly, manual, and labelled workflows retain the
|
|
107
|
+
cross-platform escape hatch, reducing routine Actions cost without narrowing supported Python
|
|
108
|
+
coverage.
|
|
109
|
+
|
|
110
|
+
## v0.4.3 — 2026-08-09
|
|
111
|
+
|
|
112
|
+
**A trust and validation-throughput patch release.** Whole-part hexagonal stock now receives its
|
|
113
|
+
manufacturing definition, assembly recognition no longer combines evidence across bodies, and
|
|
114
|
+
performance validation is both more stable and faster to run.
|
|
115
|
+
|
|
116
|
+
### Added
|
|
117
|
+
|
|
118
|
+
- **Whole-part regular hexagonal stock is recognised independently of an attached boss and
|
|
119
|
+
dimensioned as `HEX … A/F` plus its axial length** (#1082). In-plane rotation preserves the
|
|
120
|
+
geometric across-flats size instead of substituting an orientation-dependent bounding-box
|
|
121
|
+
width. Automatic and declared paths share the same compiler and placement machinery, while
|
|
122
|
+
completeness lint distinguishes placed, authored-suppressed, dropped, missing, and ambiguous
|
|
123
|
+
outcomes.
|
|
124
|
+
|
|
125
|
+
### Fixed
|
|
126
|
+
|
|
127
|
+
- **Slot and pocket patterns cannot group members from separate assembly bodies** (#1073).
|
|
128
|
+
Recognition carries solid ownership through grouping and fails closed when ownership is
|
|
129
|
+
unavailable, preventing plausible-looking phantom patterns across disconnected parts.
|
|
130
|
+
|
|
131
|
+
- **The annotation-obstacle performance guard uses deterministic work-count evidence instead of
|
|
132
|
+
a wall-clock threshold** (#1071). The regression test remains load-bearing under mutation but
|
|
133
|
+
no longer flakes because of machine load or suite ordering.
|
|
134
|
+
|
|
135
|
+
### Changed
|
|
136
|
+
|
|
137
|
+
- **The post-merge slow tier distributes individual tests across workers** (#1108), using
|
|
138
|
+
`pytest-xdist`'s load scheduling after local benchmark evidence showed a material speed-up
|
|
139
|
+
without weakening fixture coverage. Hosted slow validation remains the single post-merge gate.
|
|
140
|
+
|
|
5
141
|
## v0.4.2 — 2026-08-09
|
|
6
142
|
|
|
7
143
|
**The trustworthy manufacturing drawings release.** AP242 PMI is now accounted for from its
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: draftwright
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.4
|
|
4
4
|
Summary: Automated technical-drawing generation for build123d
|
|
5
5
|
Project-URL: Homepage, https://github.com/pzfreo/draftwright
|
|
6
6
|
Project-URL: Repository, https://github.com/pzfreo/draftwright
|
|
@@ -743,6 +743,7 @@ Choose formats, scale, and page; or emit an editable drawing script:
|
|
|
743
743
|
```
|
|
744
744
|
draftwright my_part.step --format pdf,dxf # also: svg, all
|
|
745
745
|
draftwright my_part.step --scale 2 --page A3 # override the auto scale / page
|
|
746
|
+
draftwright my_part.step --scale 1 --page A4 --scale-policy strict
|
|
746
747
|
draftwright my_part.step --script # write an editable declarative Sheet script
|
|
747
748
|
```
|
|
748
749
|
|
|
@@ -895,13 +896,18 @@ installed automatically as a dependency.
|
|
|
895
896
|
### Scale and page control
|
|
896
897
|
|
|
897
898
|
```python
|
|
898
|
-
from draftwright import choose_scale
|
|
899
|
+
from draftwright import build_drawing, choose_scale
|
|
899
900
|
|
|
900
901
|
# Auto-select the best ISO/ASME standard scale for an A3 sheet
|
|
901
902
|
scale, page_w, page_h, n_steps = choose_scale(80, 60, 20, page="A3")
|
|
902
903
|
|
|
903
|
-
# Override
|
|
904
|
-
|
|
904
|
+
# Override. Required annotations are protected by the default fallback policy.
|
|
905
|
+
dwg = build_drawing(part, scale=1.0, page="A4")
|
|
906
|
+
print(dwg.scale_decision) # requested/effective scale, attempts, blockers
|
|
907
|
+
|
|
908
|
+
# Refuse an incompatible request, or explicitly retain historical best-effort output.
|
|
909
|
+
build_drawing(part, scale=1.0, page="A4", scale_policy="strict")
|
|
910
|
+
build_drawing(part, scale=1.0, page="A4", scale_policy="permissive")
|
|
905
911
|
```
|
|
906
912
|
|
|
907
913
|
### Edit, critique, and self-repair
|
|
@@ -916,10 +922,40 @@ dwg = build_drawing(part)
|
|
|
916
922
|
env = next(f for f in dwg.model().features if f.kind == "envelope")
|
|
917
923
|
dwg.dimension(env, "length", role="width", side="below", pin=True)
|
|
918
924
|
|
|
919
|
-
crit = dwg.lint_summary()
|
|
925
|
+
crit = dwg.lint_summary()
|
|
926
|
+
# Legacy diagnostic severity heuristic (not overall drawing quality):
|
|
927
|
+
assert crit["diagnostic_score"] == crit["score"]
|
|
928
|
+
# Independently observable components; no composite quality score is invented:
|
|
929
|
+
completeness = crit["quality"]["completeness"]
|
|
930
|
+
# Named for its denominator: the requirements Draftwright recognized AND can audit.
|
|
931
|
+
# 1.0 does NOT mean the drawing is complete — it means every requirement recognition did
|
|
932
|
+
# identify was placed, however much of the part it missed. What was never recognized never
|
|
933
|
+
# became a requirement. (Recognized nothing auditable at all? available is False, not 1.0.)
|
|
934
|
+
audited = completeness["audited_score"]
|
|
935
|
+
assert completeness["scope"] == "audited_recognized_requirements"
|
|
936
|
+
completeness["excludes"] # what the denominator cannot see
|
|
937
|
+
completeness["unrecognised_geometry_reports"] # a floor on that gap, never a measure
|
|
938
|
+
legibility = crit["quality"]["legibility"]
|
|
939
|
+
# Raw pair findings remain available and keep the original count semantics.
|
|
940
|
+
legibility["warnings"], legibility["by_code"], crit["issues"]
|
|
941
|
+
# The legibility scalar uses primary issues: one affected annotation/failure mechanism even
|
|
942
|
+
# when several pairwise comparisons found it. These fields make that denominator explicit.
|
|
943
|
+
legibility["primary_issues"], legibility["primary_by_code"], legibility["affected_pairs"]
|
|
944
|
+
assert legibility["score_inventory"] == "primary_issues"
|
|
945
|
+
restraint = crit["quality"]["restraint"] # unavailable until provenance is complete
|
|
920
946
|
dwg.repair() # auto-fix mechanically-fixable lint; never worsens
|
|
921
947
|
```
|
|
922
948
|
|
|
949
|
+
Completeness is strict about authored omissions: removing a dimension from a complete authored
|
|
950
|
+
set records a `suppressed` requirement and lowers the audited score. The evidence distinguishes
|
|
951
|
+
that choice from a placement failure, but cannot certify that the omission is
|
|
952
|
+
engineering-correct; plain deletion is not an accepted-waiver mechanism.
|
|
953
|
+
|
|
954
|
+
**`audited_score` is not a completion gate.** It answers "of the requirements we recognised and
|
|
955
|
+
can audit, how many were placed?" — so a part can score 1.0 with whole features missing from the
|
|
956
|
+
drawing, if recognition never identified them. Gate on issue codes and severities, and read
|
|
957
|
+
`excludes` and `unrecognised_geometry_reports` for what the denominator cannot account for.
|
|
958
|
+
|
|
923
959
|
Each `LintIssue` carries a domain-meaningful `code` and, when computable, a
|
|
924
960
|
ready-to-apply `suggestion`. See `docs/adr/` for the design (deterministic
|
|
925
961
|
generation, the lint→repair loop, and collect-then-solve placement).
|
|
@@ -44,6 +44,7 @@ Choose formats, scale, and page; or emit an editable drawing script:
|
|
|
44
44
|
```
|
|
45
45
|
draftwright my_part.step --format pdf,dxf # also: svg, all
|
|
46
46
|
draftwright my_part.step --scale 2 --page A3 # override the auto scale / page
|
|
47
|
+
draftwright my_part.step --scale 1 --page A4 --scale-policy strict
|
|
47
48
|
draftwright my_part.step --script # write an editable declarative Sheet script
|
|
48
49
|
```
|
|
49
50
|
|
|
@@ -196,13 +197,18 @@ installed automatically as a dependency.
|
|
|
196
197
|
### Scale and page control
|
|
197
198
|
|
|
198
199
|
```python
|
|
199
|
-
from draftwright import choose_scale
|
|
200
|
+
from draftwright import build_drawing, choose_scale
|
|
200
201
|
|
|
201
202
|
# Auto-select the best ISO/ASME standard scale for an A3 sheet
|
|
202
203
|
scale, page_w, page_h, n_steps = choose_scale(80, 60, 20, page="A3")
|
|
203
204
|
|
|
204
|
-
# Override
|
|
205
|
-
|
|
205
|
+
# Override. Required annotations are protected by the default fallback policy.
|
|
206
|
+
dwg = build_drawing(part, scale=1.0, page="A4")
|
|
207
|
+
print(dwg.scale_decision) # requested/effective scale, attempts, blockers
|
|
208
|
+
|
|
209
|
+
# Refuse an incompatible request, or explicitly retain historical best-effort output.
|
|
210
|
+
build_drawing(part, scale=1.0, page="A4", scale_policy="strict")
|
|
211
|
+
build_drawing(part, scale=1.0, page="A4", scale_policy="permissive")
|
|
206
212
|
```
|
|
207
213
|
|
|
208
214
|
### Edit, critique, and self-repair
|
|
@@ -217,10 +223,40 @@ dwg = build_drawing(part)
|
|
|
217
223
|
env = next(f for f in dwg.model().features if f.kind == "envelope")
|
|
218
224
|
dwg.dimension(env, "length", role="width", side="below", pin=True)
|
|
219
225
|
|
|
220
|
-
crit = dwg.lint_summary()
|
|
226
|
+
crit = dwg.lint_summary()
|
|
227
|
+
# Legacy diagnostic severity heuristic (not overall drawing quality):
|
|
228
|
+
assert crit["diagnostic_score"] == crit["score"]
|
|
229
|
+
# Independently observable components; no composite quality score is invented:
|
|
230
|
+
completeness = crit["quality"]["completeness"]
|
|
231
|
+
# Named for its denominator: the requirements Draftwright recognized AND can audit.
|
|
232
|
+
# 1.0 does NOT mean the drawing is complete — it means every requirement recognition did
|
|
233
|
+
# identify was placed, however much of the part it missed. What was never recognized never
|
|
234
|
+
# became a requirement. (Recognized nothing auditable at all? available is False, not 1.0.)
|
|
235
|
+
audited = completeness["audited_score"]
|
|
236
|
+
assert completeness["scope"] == "audited_recognized_requirements"
|
|
237
|
+
completeness["excludes"] # what the denominator cannot see
|
|
238
|
+
completeness["unrecognised_geometry_reports"] # a floor on that gap, never a measure
|
|
239
|
+
legibility = crit["quality"]["legibility"]
|
|
240
|
+
# Raw pair findings remain available and keep the original count semantics.
|
|
241
|
+
legibility["warnings"], legibility["by_code"], crit["issues"]
|
|
242
|
+
# The legibility scalar uses primary issues: one affected annotation/failure mechanism even
|
|
243
|
+
# when several pairwise comparisons found it. These fields make that denominator explicit.
|
|
244
|
+
legibility["primary_issues"], legibility["primary_by_code"], legibility["affected_pairs"]
|
|
245
|
+
assert legibility["score_inventory"] == "primary_issues"
|
|
246
|
+
restraint = crit["quality"]["restraint"] # unavailable until provenance is complete
|
|
221
247
|
dwg.repair() # auto-fix mechanically-fixable lint; never worsens
|
|
222
248
|
```
|
|
223
249
|
|
|
250
|
+
Completeness is strict about authored omissions: removing a dimension from a complete authored
|
|
251
|
+
set records a `suppressed` requirement and lowers the audited score. The evidence distinguishes
|
|
252
|
+
that choice from a placement failure, but cannot certify that the omission is
|
|
253
|
+
engineering-correct; plain deletion is not an accepted-waiver mechanism.
|
|
254
|
+
|
|
255
|
+
**`audited_score` is not a completion gate.** It answers "of the requirements we recognised and
|
|
256
|
+
can audit, how many were placed?" — so a part can score 1.0 with whole features missing from the
|
|
257
|
+
drawing, if recognition never identified them. Gate on issue codes and severities, and read
|
|
258
|
+
`excludes` and `unrecognised_geometry_reports` for what the denominator cannot account for.
|
|
259
|
+
|
|
224
260
|
Each `LintIssue` carries a domain-meaningful `code` and, when computable, a
|
|
225
261
|
ready-to-apply `suggestion`. See `docs/adr/` for the design (deterministic
|
|
226
262
|
generation, the lint→repair loop, and collect-then-solve placement).
|
|
@@ -34,6 +34,7 @@ architecture** table; open retired or superseded records only for design history
|
|
|
34
34
|
| [0015](0015-part-drawing-compiler-as-built.md) | The part-drawing compiler, as built | Use one detected-or-declared feature IR and planner-fed dimension groups as the compiler waist. | Accepted; supersedes 0008 | `test_part_model.py`, `test_detect_once.py`, `test_import_boundaries.py` |
|
|
35
35
|
| [0016](0016-declared-dimensioning-intent.md) | Declared dimensioning intent: capture what to measure, let the engine place it | Declare which measurements matter as scale-independent intent routed through the planner and corridor solve; never hardcode dimension geometry. | Accepted; epic #867 complete; phase 6 landed (#940) | `test_compiled_plan_boundary.py`, `test_label_provenance.py`, `test_sheet_emit.py`, `test_add_dimension.py` |
|
|
36
36
|
| [0017](0017-recognition-inventory-correspondence-and-measurement-provenance.md) | One recognition result per run; correspondence is evidence-gated | Produce one explicit recognition result owned by `BuildState`; require vertical-slice evidence before generalising correspondence, identity, requirements, outcomes, or reconciliation. | Accepted; ownership phase landed, extensions gated by #1018 | `test_recognition_manifest.py`, `test_recognition_result.py`, `test_declared_recognition_gate.py` |
|
|
37
|
+
| [0018](0018-requirement-driven-view-planning-and-editable-sheet-layout.md) | Requirement-driven view planning and editable sheet layout | Use one `ViewSpec` vocabulary and planner with distinct authored `ViewConstraints` and immutable `ResolvedViewPlan`; jointly validate views, typography, convention, scale, paper and layout against requirement survival. If accepted, supersedes ADR 0004's fixed-topology assumption while retaining compose-then-pack. | Proposed; tracked by #1130 | Required guards are listed in the ADR |
|
|
37
38
|
|
|
38
39
|
## Historical records
|
|
39
40
|
|
|
@@ -47,8 +48,8 @@ architecture** table; open retired or superseded records only for design history
|
|
|
47
48
|
|
|
48
49
|
- Compiler and state ownership: 0001 → 0005 → 0015.
|
|
49
50
|
- Recognition and public declaration: 0007 → 0013 → 0011 → 0015.
|
|
50
|
-
- Layout and placement: 0004 → 0014 → 0012.
|
|
51
|
-
- Declared intent and the editable surface: 0001 → 0011 → 0012 → 0016.
|
|
51
|
+
- Layout and placement: 0004 → 0014 → 0012 → 0018.
|
|
52
|
+
- Declared intent and the editable surface: 0001 → 0011 → 0012 → 0016 → 0018.
|
|
52
53
|
- Quality and correction: 0002, with provenance from 0010.
|
|
53
54
|
- Recognition correspondence and completeness: 0007 → 0013 → 0015 → 0017.
|
|
54
55
|
|
|
@@ -24,6 +24,7 @@ from typing import TYPE_CHECKING
|
|
|
24
24
|
# on every TAB press (#313). Each name maps to the submodule that provides it.
|
|
25
25
|
_LAZY = {
|
|
26
26
|
"build_drawing": "draftwright.builder",
|
|
27
|
+
"ScaleIncompatibilityError": "draftwright.builder",
|
|
27
28
|
"make_drawing": "draftwright.builder",
|
|
28
29
|
"Drawing": "draftwright.drawing",
|
|
29
30
|
"FeatureInfo": "draftwright.drawing",
|
|
@@ -40,6 +41,7 @@ _LAZY = {
|
|
|
40
41
|
# dependency-free `_warnings` leaf for that second reason: defined in `_core` it cost ~6 s
|
|
41
42
|
# to reach, and the pytest filterwarnings entry naming it paid that on every invocation.
|
|
42
43
|
"SoftDeprecationWarning": "draftwright._warnings",
|
|
44
|
+
"ScaleCompletenessWarning": "draftwright._warnings",
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
|
|
@@ -76,8 +78,8 @@ _sys.modules[__name__].__class__ = _DraftwrightModule
|
|
|
76
78
|
|
|
77
79
|
|
|
78
80
|
if TYPE_CHECKING: # static analysers / IDEs — no runtime import, no kernel cost
|
|
79
|
-
from draftwright._warnings import SoftDeprecationWarning
|
|
80
|
-
from draftwright.builder import build_drawing, make_drawing
|
|
81
|
+
from draftwright._warnings import ScaleCompletenessWarning, SoftDeprecationWarning
|
|
82
|
+
from draftwright.builder import ScaleIncompatibilityError, build_drawing, make_drawing
|
|
81
83
|
from draftwright.compose import choose_scale
|
|
82
84
|
from draftwright.drawing import Drawing, FeatureInfo
|
|
83
85
|
from draftwright.linting import lint_feature_coverage
|
|
@@ -106,6 +108,8 @@ __all__ = [
|
|
|
106
108
|
"PmiRecord",
|
|
107
109
|
"PmiSourceEntity",
|
|
108
110
|
"Sheet",
|
|
111
|
+
"ScaleIncompatibilityError",
|
|
112
|
+
"ScaleCompletenessWarning",
|
|
109
113
|
"build_drawing",
|
|
110
114
|
"choose_scale",
|
|
111
115
|
"extract_pmi",
|
|
@@ -280,6 +280,22 @@ def _tag_sequence(n):
|
|
|
280
280
|
|
|
281
281
|
_DIAM_RE = re.compile(r"[øØ⌀]\s*(\d+(?:\.\d+)?)")
|
|
282
282
|
|
|
283
|
+
|
|
284
|
+
def _annotation_diameter_sources(annotation) -> tuple[tuple[float, ...], tuple[float, ...]]:
|
|
285
|
+
"""Return ``(structured, text)`` diameter mentions for one annotation.
|
|
286
|
+
|
|
287
|
+
Engine-produced annotations with ``covers_diameters`` describe their physical-feature
|
|
288
|
+
correspondence structurally. Their formatted labels may contain other diameters (for
|
|
289
|
+
example ``ø60 BC``), so text parsing is a legacy/unstructured fallback and is never
|
|
290
|
+
combined with structured coverage (ADR 0017 / #1142).
|
|
291
|
+
"""
|
|
292
|
+
structured = tuple(float(value) for value in getattr(annotation, "covers_diameters", ()))
|
|
293
|
+
if structured:
|
|
294
|
+
return structured, ()
|
|
295
|
+
label = getattr(annotation, "label", None) or ""
|
|
296
|
+
return (), tuple(float(match.group(1)) for match in _DIAM_RE.finditer(label))
|
|
297
|
+
|
|
298
|
+
|
|
283
299
|
# A single-quoted label lifted from a lint message, e.g. "labels 'A' and 'B' …".
|
|
284
300
|
# Shared by the #29 lint suggestions (linting.py) and the #30 repair loop.
|
|
285
301
|
_QUOTED_RE = re.compile(r"'([^']*)'")
|
|
@@ -981,6 +997,7 @@ class Analysis:
|
|
|
981
997
|
layout_n_steps: int
|
|
982
998
|
layout_section: bool
|
|
983
999
|
layout_table_sizes: tuple[tuple[float, float], ...]
|
|
1000
|
+
layout_required_tables: tuple[tuple[tuple[float, float], str], ...]
|
|
984
1001
|
sv_right: float
|
|
985
1002
|
iso_right_limit: float
|
|
986
1003
|
SCALE: float
|
|
@@ -109,6 +109,18 @@ def _axis_letter_of(axis) -> str:
|
|
|
109
109
|
return max(zip("xyz", axis, strict=True), key=lambda t: abs(t[1]))[0]
|
|
110
110
|
|
|
111
111
|
|
|
112
|
+
def _is_principal_axis(axis) -> bool:
|
|
113
|
+
"""Whether *axis* is exactly along X, Y or Z after analytic-noise snapping.
|
|
114
|
+
|
|
115
|
+
``Frame.axis`` can name only these directions. Exactness is evaluated after the same
|
|
116
|
+
``1e-6`` component snap used for analytic STEP noise. A cosine-tolerance test would
|
|
117
|
+
admit a small but real second component (and millimetres of flattening over a long
|
|
118
|
+
part); requiring exactly one non-zero component expresses the representable contract.
|
|
119
|
+
"""
|
|
120
|
+
snapped = [0.0 if abs(float(component)) < 1e-6 else float(component) for component in axis]
|
|
121
|
+
return sum(component != 0.0 for component in snapped) == 1
|
|
122
|
+
|
|
123
|
+
|
|
112
124
|
def _axis_direction_components(axis: str, direction=None):
|
|
113
125
|
"""Validate a direction and return ``(raw, norm, dominant_index)``."""
|
|
114
126
|
if axis not in "xyz" or len(axis) != 1:
|
|
@@ -269,11 +281,14 @@ def _segment_crosses_box(p1, p2, box) -> bool:
|
|
|
269
281
|
return any(_seg_seg(p1, p2, corners[i], corners[(i + 1) % 4]) for i in range(4))
|
|
270
282
|
|
|
271
283
|
|
|
272
|
-
def
|
|
273
|
-
"""
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
284
|
+
def _segment_clip_extent(p, q, box, pad=0.0):
|
|
285
|
+
"""AABB of the part of *p*→*q* inside the *pad*-inflated *box*.
|
|
286
|
+
|
|
287
|
+
Return ``None`` when the segment is disjoint. The Liang–Barsky clip is
|
|
288
|
+
inclusive at the boundary: lint uses the returned extent both as its
|
|
289
|
+
tolerant hit gate and to measure only the rendered part that actually
|
|
290
|
+
reaches a label (#1144).
|
|
291
|
+
"""
|
|
277
292
|
minx, miny, maxx, maxy = box[0] - pad, box[1] - pad, box[2] + pad, box[3] + pad
|
|
278
293
|
x0, y0 = p
|
|
279
294
|
dx, dy = q[0] - x0, q[1] - y0
|
|
@@ -281,15 +296,90 @@ def _segment_clips_box(p, q, box, pad=0.0) -> bool:
|
|
|
281
296
|
for pp, qq in ((-dx, x0 - minx), (dx, maxx - x0), (-dy, y0 - miny), (dy, maxy - y0)):
|
|
282
297
|
if abs(pp) < 1e-12:
|
|
283
298
|
if qq < 0:
|
|
284
|
-
return
|
|
299
|
+
return None
|
|
285
300
|
else:
|
|
286
301
|
r = qq / pp
|
|
287
302
|
if pp < 0:
|
|
288
303
|
if r > t1:
|
|
289
|
-
return
|
|
304
|
+
return None
|
|
290
305
|
t0 = max(t0, r)
|
|
291
306
|
else:
|
|
292
307
|
if r < t0:
|
|
293
|
-
return
|
|
308
|
+
return None
|
|
294
309
|
t1 = min(t1, r)
|
|
295
|
-
|
|
310
|
+
if t0 > t1:
|
|
311
|
+
return None
|
|
312
|
+
first = (x0 + t0 * dx, y0 + t0 * dy)
|
|
313
|
+
second = (x0 + t1 * dx, y0 + t1 * dy)
|
|
314
|
+
return (
|
|
315
|
+
min(first[0], second[0]),
|
|
316
|
+
min(first[1], second[1]),
|
|
317
|
+
max(first[0], second[0]),
|
|
318
|
+
max(first[1], second[1]),
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def _segment_clips_box(p, q, box, pad=0.0) -> bool:
|
|
323
|
+
"""Liang–Barsky: does segment *p*→*q* intersect the *pad*-inflated AABB
|
|
324
|
+
*box*? Inclusive at the boundary (a touch is a hit) — the tolerant form the
|
|
325
|
+
lint checks use; :func:`_segment_crosses_box` is the strict placement-side
|
|
326
|
+
sibling (#700)."""
|
|
327
|
+
return _segment_clip_extent(p, q, box, pad=pad) is not None
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def _segments_cross_or_overlap(a1, a2, b1, b2) -> bool:
|
|
331
|
+
"""Whether two page-plane segments cross or overlap beyond a shared endpoint.
|
|
332
|
+
|
|
333
|
+
Proper interior crossings, a T-junction into the other segment's interior,
|
|
334
|
+
and collinear overlap are conflicts. Merely sharing one endpoint is not: two
|
|
335
|
+
leaders may legitimately terminate at the same feature/ring boundary.
|
|
336
|
+
"""
|
|
337
|
+
epsilon = 1e-9
|
|
338
|
+
|
|
339
|
+
def cross(origin, first, second):
|
|
340
|
+
return (first[0] - origin[0]) * (second[1] - origin[1]) - (first[1] - origin[1]) * (
|
|
341
|
+
second[0] - origin[0]
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
def same(first, second):
|
|
345
|
+
return abs(first[0] - second[0]) <= epsilon and abs(first[1] - second[1]) <= epsilon
|
|
346
|
+
|
|
347
|
+
def on_segment(point, first, second):
|
|
348
|
+
return (
|
|
349
|
+
abs(cross(first, second, point)) <= epsilon
|
|
350
|
+
and min(first[0], second[0]) - epsilon
|
|
351
|
+
<= point[0]
|
|
352
|
+
<= max(first[0], second[0]) + epsilon
|
|
353
|
+
and min(first[1], second[1]) - epsilon
|
|
354
|
+
<= point[1]
|
|
355
|
+
<= max(first[1], second[1]) + epsilon
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
oa1 = cross(b1, b2, a1)
|
|
359
|
+
oa2 = cross(b1, b2, a2)
|
|
360
|
+
ob1 = cross(a1, a2, b1)
|
|
361
|
+
ob2 = cross(a1, a2, b2)
|
|
362
|
+
if oa1 * oa2 < -(epsilon**2) and ob1 * ob2 < -(epsilon**2):
|
|
363
|
+
return True
|
|
364
|
+
|
|
365
|
+
if all(abs(value) <= epsilon for value in (oa1, oa2, ob1, ob2)):
|
|
366
|
+
axis = (
|
|
367
|
+
0
|
|
368
|
+
if max(abs(a2[0] - a1[0]), abs(b2[0] - b1[0]))
|
|
369
|
+
>= max(abs(a2[1] - a1[1]), abs(b2[1] - b1[1]))
|
|
370
|
+
else 1
|
|
371
|
+
)
|
|
372
|
+
overlap = min(max(a1[axis], a2[axis]), max(b1[axis], b2[axis])) - max(
|
|
373
|
+
min(a1[axis], a2[axis]), min(b1[axis], b2[axis])
|
|
374
|
+
)
|
|
375
|
+
return bool(overlap > epsilon)
|
|
376
|
+
|
|
377
|
+
for point, first, second in (
|
|
378
|
+
(a1, b1, b2),
|
|
379
|
+
(a2, b1, b2),
|
|
380
|
+
(b1, a1, a2),
|
|
381
|
+
(b2, a1, a2),
|
|
382
|
+
):
|
|
383
|
+
if on_segment(point, first, second) and not (same(point, first) or same(point, second)):
|
|
384
|
+
return True
|
|
385
|
+
return False
|
|
@@ -37,3 +37,11 @@ class SoftDeprecationWarning(UserWarning):
|
|
|
37
37
|
in notebooks, several test runners, and library-internal call paths, whereas this shows
|
|
38
38
|
unconditionally.
|
|
39
39
|
"""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ScaleCompletenessWarning(UserWarning):
|
|
43
|
+
"""An explicit scale was changed or honored with required placement loss (#1146).
|
|
44
|
+
|
|
45
|
+
Callers can filter this policy decision independently of unrelated user warnings while
|
|
46
|
+
still inspecting the complete machine-readable record on ``Drawing.scale_decision``.
|
|
47
|
+
"""
|