draftwright 0.4.5__tar.gz → 0.4.6__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.5 → draftwright-0.4.6}/CHANGELOG.md +25 -1
- {draftwright-0.4.5 → draftwright-0.4.6}/PKG-INFO +9 -5
- {draftwright-0.4.5 → draftwright-0.4.6}/README.md +7 -4
- {draftwright-0.4.5 → draftwright-0.4.6}/docs/adr/README.md +3 -3
- {draftwright-0.4.5 → draftwright-0.4.6}/pyproject.toml +3 -1
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/_core.py +2 -1
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/_geometry.py +88 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/analysis.py +8 -8
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/annotations/from_model.py +3 -2
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/annotations/holes.py +17 -8
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/drawing.py +15 -6
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/linting/channel_coverage.py +2 -1
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/linting/coverage.py +15 -15
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/linting/flat_coverage.py +2 -1
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/linting/hole_coverage.py +2 -1
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/linting/polygonal_stock_coverage.py +2 -1
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/linting/profiled_bore_coverage.py +2 -1
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/linting/slot_coverage.py +2 -1
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/model/compiled.py +8 -1
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/model/declare.py +8 -9
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/model/detect.py +35 -34
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/model/ir.py +19 -1
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/model/planner.py +3 -0
- draftwright-0.4.6/src/draftwright/recognition/__init__.py +15 -0
- draftwright-0.4.6/src/draftwright/recognition_cache.py +26 -0
- draftwright-0.4.6/src/draftwright/score.py +10 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_declare.py +13 -17
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_declared_recognition_gate.py +9 -9
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_detect_once.py +2 -2
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_detect_registry.py +2 -2
- draftwright-0.4.6/tests/test_external_recognition_boundary.py +69 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_fillets_adjacency.py +1 -2
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_flat_completeness.py +2 -2
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_flat_stock_identity.py +21 -5
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_flat_stock_opposition.py +1 -2
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_grid_lattice_convention.py +3 -3
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_import_boundaries.py +1 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_1058_wheel_profile.py +23 -23
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_1073_cross_body_patterns.py +5 -5
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_1082_polygonal_stock.py +5 -5
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_1143_hole_completeness.py +7 -7
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_1146_scale_completeness.py +3 -3
- draftwright-0.4.6/tests/test_issue_367_leader_ink.py +137 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_676_polygonal_boss.py +2 -2
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_885_prismatic_coverage.py +1 -1
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_909_sloped_profile.py +1 -1
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_917_open_channel.py +1 -1
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_958_cross_solid_recognition.py +4 -4
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_make_drawing.py +34 -46
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_part_model.py +1 -1
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_pocket_pattern_recognition.py +9 -9
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_private_test_imports.py +5 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_quality_components.py +2 -1
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_recogniser_contract.py +3 -4
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_recognition.py +28 -30
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_recognition_manifest.py +14 -14
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_recognition_result.py +9 -8
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_slanted_blind_step.py +3 -3
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_slot_completeness.py +1 -1
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_slot_pattern_recognition.py +6 -6
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_turned_steps.py +1 -2
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_workflows.py +14 -0
- draftwright-0.4.5/src/draftwright/recognition/__init__.py +0 -205
- draftwright-0.4.5/src/draftwright/recognition/_features.py +0 -1164
- draftwright-0.4.5/src/draftwright/recognition/_record.py +0 -28
- draftwright-0.4.5/src/draftwright/recognition/chamfers.py +0 -196
- draftwright-0.4.5/src/draftwright/recognition/countersinks.py +0 -206
- draftwright-0.4.5/src/draftwright/recognition/fillets.py +0 -183
- draftwright-0.4.5/src/draftwright/recognition/flats.py +0 -275
- draftwright-0.4.5/src/draftwright/recognition/grooves.py +0 -145
- draftwright-0.4.5/src/draftwright/recognition/levels.py +0 -346
- draftwright-0.4.5/src/draftwright/recognition/pads.py +0 -148
- draftwright-0.4.5/src/draftwright/recognition/plates.py +0 -154
- draftwright-0.4.5/src/draftwright/recognition/polygonal_bosses.py +0 -342
- draftwright-0.4.5/src/draftwright/recognition/profiled_bores.py +0 -368
- draftwright-0.4.5/src/draftwright/recognition/repeating_profiles.py +0 -393
- draftwright-0.4.5/src/draftwright/recognition/result.py +0 -293
- draftwright-0.4.5/src/draftwright/recognition/slots.py +0 -1465
- draftwright-0.4.5/src/draftwright/recognition/turned.py +0 -202
- draftwright-0.4.5/src/draftwright/score.py +0 -76
- {draftwright-0.4.5 → draftwright-0.4.6}/.gitignore +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/LICENSE +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/skills/SKILL.md +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/__init__.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/_pmi_part21.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/_warnings.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/annotate.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/annotations/__init__.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/annotations/_common.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/annotations/balloons.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/annotations/gears.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/annotations/orchestrator.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/annotations/sections.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/audit.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/builder.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/cli.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/compose.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/export.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/fits.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/fonts/IBMPlexMono-Regular.ttf +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/fonts/IBMPlexSansCondensed-Regular.ttf +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/fonts/LICENSE-IBMPlexMono-OFL.txt +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/fonts/LICENSE-IBMPlexSansCondensed-OFL.txt +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/fonts/__init__.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/intents.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/layout.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/linting/__init__.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/linting/gear_coverage.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/linting/issues.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/linting/pmi_coverage.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/linting/quality.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/linting/structural.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/linting/suggest.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/make_drawing.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/model/__init__.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/model/callout.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/pmi.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/projection.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/py.typed +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/registry.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/repair.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/sheet.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/src/draftwright/sheet_emit.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/_kernel.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/_layout_sig.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/conftest.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/fixtures/grm03_thumbwheel_drive_screw.step +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/fixtures/if_step_flat_across_cylinder.step +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/fixtures/issue_1058_wheel_rh.step +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/fixtures/issue_909_basic_part_design_017_body.step +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/fixtures/issue_915_case_study_2.step +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/fixtures/nist_ctc_01_asme1_ap203.stp +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/fixtures/nist_ctc_01_asme1_ap242.stp +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/fixtures/nist_ctc_02_asme1_ap203.stp +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/fixtures/nist_ctc_02_asme1_ap242.stp +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/fixtures/nist_ctc_03_asme1_ap203.stp +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/fixtures/nist_ctc_03_asme1_ap242.stp +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/fixtures/nist_ctc_04_asme1_ap203.stp +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/fixtures/nist_ctc_04_asme1_ap242.stp +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/fixtures/nist_ctc_05_asme1_ap203.stp +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/fixtures/nist_ctc_05_asme1_ap242.stp +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/fixtures/tuner_jig_blind_obround_pockets.step +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/refactor_golden/bracket_section.json +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/refactor_golden/centered_rebate.json +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/refactor_golden/chamfered.json +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/refactor_golden/filleted.json +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/refactor_golden/flange_dense.json +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/refactor_golden/grid_plate.json +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/refactor_golden/grooved_shaft.json +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/refactor_golden/hex_bar.json +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/refactor_golden/holed_slot.json +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/refactor_golden/pocketed.json +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/refactor_golden/prismatic_ladder.json +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/refactor_golden/scattered_plate.json +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/refactor_golden/side_drilled.json +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/refactor_golden/turned_stepped.json +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_add_dimension.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_agents_guide.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_api_docs.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_architecture_docs.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_audit_differential.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_balloon_ring_standoff.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_carve_free_position_callers.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_compiled_plan_boundary.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_counting_calls.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_dense_sheet_canary.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_deprecation_dates.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_dimension_role_vocabulary.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_drawing_encapsulation.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_e2e_slice.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_e2e_standards.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_export_dxf_curves.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_export_dxf_zoom.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_fits.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_gdt_placement.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_1000_envelope_reuse.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_1086_declared_gears.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_1142_hole_leader_labels.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_1144_transactional_hole_table.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_1147_legibility_pair_aggregation.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_676_polygonal_boss_declare.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_915_dense_case.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_916_pocket_leader.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_issue_955_height_contingency.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_label_provenance.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_layout.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_layout_cleanliness.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_layout_hypothesis.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_layout_property.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_leader_footprint.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_lint_box_cache.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_lint_reconciliation.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_lint_structural.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_linting.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_location_vocabulary.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_note_verb.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_object_aspects.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_occupancy_boxes.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_parameter_id.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_pitch_dim_footprint.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_pmi.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_pmi_datum_lowering.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_pmi_gtol_lowering.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_pmi_part21.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_pocket_pattern.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_principal_profile_classifier.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_private_test_attr_reads.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_refactor_golden.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_registry.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_render_seam.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_repack_geometry_seam.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_score.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_script_detail_parity.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_shared_box_memo.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_sheet_emit.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_sheet_gdt.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_sheet_identity_invariant.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_sheet_notes.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_sheet_of.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_sheet_section.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_sheet_tables.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_slot_pattern.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_soft_deprecation.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_solve_trace.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_strip_layout.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_suppression_ledger.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_suppression_marks.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_tolerances.py +0 -0
- {draftwright-0.4.5 → draftwright-0.4.6}/tests/test_witness_label_reconciliation.py +0 -0
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## v0.4.6 — 2026-08-15
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Geometry recognition is supplied by the standalone Apache-2.0
|
|
8
|
+
[`b123d-recognisers`](https://github.com/pzfreo/b123d-recognisers) package.** The cutover
|
|
9
|
+
pins the published stable release `v0.1.0` (built from commit
|
|
10
|
+
`9e622716c14d491729b5191aa6e5d8351c982a51`), removes the
|
|
11
|
+
duplicate embedded implementation, and keeps only identity-preserving
|
|
12
|
+
`draftwright.recognition` / `draftwright.score` compatibility re-exports until 0.6.0.
|
|
13
|
+
Draftwright continues to own the one-result build/lint cache, record→IR conversion, drafting
|
|
14
|
+
policy, placement, and critique. The package migration is manifest-pinned and golden-backed;
|
|
15
|
+
its sole normalization makes an exact dominant-axis tie deterministic across operating
|
|
16
|
+
systems without changing feature policy.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Hole-callout obstacle checks now account for the rendered leader shaft width and
|
|
21
|
+
tip-local arrowhead flare instead of treating the leader as a zero-width centreline
|
|
22
|
+
(#367). The analytical check remains geometry-only and does not over-reserve the
|
|
23
|
+
arrowhead's clearance along the whole shaft.
|
|
24
|
+
- Exact X/Z slanted flats retain their established lint-clean side-view callout after the
|
|
25
|
+
recogniser package's cross-platform semantic-axis normalization. The normalized recognition
|
|
26
|
+
record remains unchanged; `FlatFeature.presentation_axis` carries the downstream drawing-view
|
|
27
|
+
choice, preventing a leader/silhouette crossing without duplicating recognition.
|
|
4
28
|
|
|
5
29
|
## v0.4.5 — 2026-08-15
|
|
6
30
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: draftwright
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.6
|
|
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
|
|
@@ -684,6 +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.1.0
|
|
687
688
|
Requires-Dist: build123d-drafting-helpers>=0.14.1
|
|
688
689
|
Requires-Dist: build123d<0.11,>=0.9.0; python_full_version < '3.13'
|
|
689
690
|
Requires-Dist: build123d<0.12,>=0.11; python_full_version >= '3.13'
|
|
@@ -967,17 +968,20 @@ or declared features converge on a `PartModel` IR, then planner-fed and
|
|
|
967
968
|
sanctioned model-routed render intents feed shared placement, projection, and
|
|
968
969
|
export. Coverage lint independently compares recognised geometry with the
|
|
969
970
|
placed drawing, so an upstream omission cannot hide from verification. It
|
|
970
|
-
builds on
|
|
971
|
+
builds on three libraries:
|
|
971
972
|
|
|
972
973
|
```
|
|
973
974
|
draftwright
|
|
975
|
+
└── b123d-recognisers — deterministic geometry recognition
|
|
974
976
|
└── build123d-drafting-helpers — Dimension, Leader, HoleCallout, …
|
|
975
977
|
└── build123d — CAD kernel
|
|
976
978
|
```
|
|
977
979
|
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
980
|
+
Geometry recognition lives in the Apache-2.0 `b123d-recognisers` package. Draftwright owns
|
|
981
|
+
the per-build recognition cache, record→IR conversion, drafting policy, and linting
|
|
982
|
+
(`linting/`); annotation primitives (`Dimension`, `Leader`, etc.) live in
|
|
983
|
+
`build123d-drafting-helpers` and can be used independently. The compiler is largely converged
|
|
984
|
+
in production — turned
|
|
981
985
|
dims/lengths, centre marks, envelope, slots, holes (callouts/locations/grouping),
|
|
982
986
|
the section A–A trigger, the prismatic step-ladder + rotational furniture, and PMI/GD&T
|
|
983
987
|
are all on the IR. See
|
|
@@ -268,17 +268,20 @@ or declared features converge on a `PartModel` IR, then planner-fed and
|
|
|
268
268
|
sanctioned model-routed render intents feed shared placement, projection, and
|
|
269
269
|
export. Coverage lint independently compares recognised geometry with the
|
|
270
270
|
placed drawing, so an upstream omission cannot hide from verification. It
|
|
271
|
-
builds on
|
|
271
|
+
builds on three libraries:
|
|
272
272
|
|
|
273
273
|
```
|
|
274
274
|
draftwright
|
|
275
|
+
└── b123d-recognisers — deterministic geometry recognition
|
|
275
276
|
└── build123d-drafting-helpers — Dimension, Leader, HoleCallout, …
|
|
276
277
|
└── build123d — CAD kernel
|
|
277
278
|
```
|
|
278
279
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
280
|
+
Geometry recognition lives in the Apache-2.0 `b123d-recognisers` package. Draftwright owns
|
|
281
|
+
the per-build recognition cache, record→IR conversion, drafting policy, and linting
|
|
282
|
+
(`linting/`); annotation primitives (`Dimension`, `Leader`, etc.) live in
|
|
283
|
+
`build123d-drafting-helpers` and can be used independently. The compiler is largely converged
|
|
284
|
+
in production — turned
|
|
282
285
|
dims/lengths, centre marks, envelope, slots, holes (callouts/locations/grouping),
|
|
283
286
|
the section A–A trigger, the prismatic step-ladder + rotational furniture, and PMI/GD&T
|
|
284
287
|
are all on the IR. See
|
|
@@ -25,15 +25,15 @@ architecture** table; open retired or superseded records only for design history
|
|
|
25
25
|
| [0004](0004-compose-then-pack-view-blocks.md) | Compose-then-pack: views as blocks carrying their annotation footprint | Compose each view with its annotation footprint, then pack fixed-topology blocks. | Accepted | `test_layout.py`, `test_layout_cleanliness.py`, `test_refactor_golden.py` |
|
|
26
26
|
| [0005](0005-pipeline-architecture-and-state-ownership.md) | Compiler-pipeline module boundaries and single-owner build state | Give compiler stages explicit module homes and build-time state explicit owners. | Accepted; implemented; compatibility aliases tracked by #720 | `test_import_boundaries.py`, `test_drawing_encapsulation.py`, `test_registry.py` |
|
|
27
27
|
| [0006](0006-deterministic-layout-via-bundled-fonts.md) | Deterministic cross-platform layout via bundled, path-pinned fonts | Pin bundled font files so text measurement and layout are cross-platform deterministic. | Accepted | `test_refactor_golden.py`, `test_layout_cleanliness.py` |
|
|
28
|
-
| [0007](0007-own-recognition-and-linting.md) | draftwright owns feature recognition and linting; helpers becomes the rendering library |
|
|
28
|
+
| [0007](0007-own-recognition-and-linting.md) | draftwright owns feature recognition and linting; helpers becomes the rendering library | Draftwright owns linting, recognition lifecycle, IR conversion, and drafting policy; `b123d-recognisers` owns geometry recognition; helpers remains the rendering library. | Accepted; extraction deployed by Amendment 2 | `test_import_boundaries.py`, `test_external_recognition_boundary.py`, `test_linting.py` |
|
|
29
29
|
| [0010](0010-annotation-provenance-seam.md) | Annotation provenance: record intent → annotation once, at the render seam | Record annotation provenance once at the render/add seam. | Accepted; landed | `test_render_seam.py`, `test_registry.py` |
|
|
30
30
|
| [0011](0011-ir-as-public-input.md) | The IR as a public input: declare features, don't only detect them | Accept the feature IR as public input through declarations and the `Sheet` façade. | Accepted; core landed; #62/#462/#495 remain | `test_declare.py`, `test_object_aspects.py`, `test_sheet_gdt.py` |
|
|
31
31
|
| [0012](0012-edits-as-pinned-priority-candidates-in-the-global-solve.md) | User annotation edits are pinned, priority-ranked corridor candidates | Drain recorded semantic edits through corridor placement with pin and priority. | Accepted; partially landed; full recomposition/parity remains #426/#707 (#661 detail views landed) | `test_make_drawing.py`, `test_sheet_emit.py` |
|
|
32
|
-
| [0013](0013-uniform-recognition-and-shared-package.md) | A uniform recogniser/feature contract
|
|
32
|
+
| [0013](0013-uniform-recognition-and-shared-package.md) | A uniform recogniser/feature contract and shared `b123d-recognisers` deployment | Enforce the geometry-only contract in the shared package and consume it through Draftwright's record→IR boundary. | Accepted; Phases 1–2 deployed | `test_external_recognition_boundary.py`, `test_detect_registry.py`, package semantic goldens |
|
|
33
33
|
| [0014](0014-collect-then-solve-annotation-placement.md) | Collect-then-solve annotation placement (as built) | Collect, select, assign, and deterministically solve annotations per corridor before rendering. | Accepted; supersedes 0009 | `test_carve_free_position_callers.py`, `test_strip_layout.py`, `test_layout_property.py`, `test_import_boundaries.py` |
|
|
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
|
-
| [0017](0017-recognition-inventory-correspondence-and-measurement-provenance.md) | One recognition result per run; correspondence is evidence-gated | Produce one
|
|
36
|
+
| [0017](0017-recognition-inventory-correspondence-and-measurement-provenance.md) | One recognition result per run; correspondence is evidence-gated | Produce one external immutable recognition result held by Draftwright's per-run cache; require vertical-slice evidence before generalising correspondence, identity, requirements, outcomes, or reconciliation. | Accepted; external cache ownership clarified, extensions gated by #1018 | `test_external_recognition_boundary.py`, `test_recognition_manifest.py`, `test_declared_recognition_gate.py` |
|
|
37
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 |
|
|
38
38
|
|
|
39
39
|
## Historical records
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "draftwright"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.6"
|
|
8
8
|
description = "Automated technical-drawing generation for build123d"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -18,6 +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.1",
|
|
21
|
+
"b123d-recognisers==0.1.0",
|
|
21
22
|
"numpy>=1.24",
|
|
22
23
|
# PNG raster export (SVG→PDF→PNG). Both permissively licensed (Pillow HPND; pypdfium2
|
|
23
24
|
# Apache-2.0 wrapping Google PDFium BSD-3) and ship pre-built wheels with NO native system
|
|
@@ -163,6 +164,7 @@ dev = [
|
|
|
163
164
|
"pytest-timeout>=2",
|
|
164
165
|
"pytest-xdist>=3",
|
|
165
166
|
"ruff>=0.4",
|
|
167
|
+
"tomli>=2; python_version < '3.11'",
|
|
166
168
|
]
|
|
167
169
|
|
|
168
170
|
[tool.codespell]
|
|
@@ -25,8 +25,9 @@ from types import SimpleNamespace
|
|
|
25
25
|
from typing import TYPE_CHECKING, Literal
|
|
26
26
|
|
|
27
27
|
if TYPE_CHECKING:
|
|
28
|
+
from b123d_recognisers import RecognitionResult, TurnedProfile
|
|
29
|
+
|
|
28
30
|
from draftwright.compose import StripDepths
|
|
29
|
-
from draftwright.recognition import RecognitionResult, TurnedProfile
|
|
30
31
|
|
|
31
32
|
from build123d import Align, BoundBox, Compound, Edge, Location, Mode, Shape, Text, Vector
|
|
32
33
|
from build123d_drafting.helpers import (
|
|
@@ -327,6 +327,94 @@ def _segment_clips_box(p, q, box, pad=0.0) -> bool:
|
|
|
327
327
|
return _segment_clip_extent(p, q, box, pad=pad) is not None
|
|
328
328
|
|
|
329
329
|
|
|
330
|
+
def _convex_polygon_overlaps_box(points, box) -> bool:
|
|
331
|
+
"""Whether a convex page-plane polygon has positive-area overlap with an AABB.
|
|
332
|
+
|
|
333
|
+
This is the small separating-axis primitive used by
|
|
334
|
+
:func:`_leader_ink_crosses_box`. Keeping it here makes the rendered-ink model pure
|
|
335
|
+
rectangle/vector maths: placement does not build temporary OCC geometry merely to ask
|
|
336
|
+
whether a candidate is clear (ADRs 0004/0014).
|
|
337
|
+
"""
|
|
338
|
+
corners = (
|
|
339
|
+
(box[0], box[1]),
|
|
340
|
+
(box[2], box[1]),
|
|
341
|
+
(box[2], box[3]),
|
|
342
|
+
(box[0], box[3]),
|
|
343
|
+
)
|
|
344
|
+
axes = [(1.0, 0.0), (0.0, 1.0)]
|
|
345
|
+
axes.extend(
|
|
346
|
+
(-(second[1] - first[1]), second[0] - first[0])
|
|
347
|
+
for first, second in zip(points, (*points[1:], points[0]), strict=True)
|
|
348
|
+
)
|
|
349
|
+
for ax, ay in axes:
|
|
350
|
+
if abs(ax) + abs(ay) <= 1e-12:
|
|
351
|
+
continue
|
|
352
|
+
polygon_projection = [x * ax + y * ay for x, y in points]
|
|
353
|
+
box_projection = [x * ax + y * ay for x, y in corners]
|
|
354
|
+
if max(polygon_projection) <= min(box_projection) or max(box_projection) <= min(
|
|
355
|
+
polygon_projection
|
|
356
|
+
):
|
|
357
|
+
return False
|
|
358
|
+
return True
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def _leader_ink_crosses_box(
|
|
362
|
+
tip,
|
|
363
|
+
elbow,
|
|
364
|
+
box,
|
|
365
|
+
*,
|
|
366
|
+
arrow_length: float,
|
|
367
|
+
line_width: float,
|
|
368
|
+
) -> bool:
|
|
369
|
+
"""Whether a rendered leader's tip-to-elbow ink overlaps *box*.
|
|
370
|
+
|
|
371
|
+
A leader is not a zero-width segment. Its shaft is a swept rectangle of
|
|
372
|
+
``line_width`` and each supported arrowhead style is contained by the triangle from
|
|
373
|
+
the tip to a base ``arrow_length`` along the shaft, with the helper's
|
|
374
|
+
``arrow_length / 3`` lateral flare. Testing those two local convex footprints keeps
|
|
375
|
+
the arrow clearance near the tip; inflating the entire shaft by the arrow size would
|
|
376
|
+
reject genuinely clear obstacles near the elbow (#367).
|
|
377
|
+
|
|
378
|
+
Touching boundaries are clear, matching :func:`_boxes_overlap` and the placement-side
|
|
379
|
+
semantics of :func:`_segment_crosses_box`.
|
|
380
|
+
"""
|
|
381
|
+
dx, dy = float(elbow[0]) - float(tip[0]), float(elbow[1]) - float(tip[1])
|
|
382
|
+
length = math.hypot(dx, dy)
|
|
383
|
+
half_line = max(0.0, float(line_width)) / 2.0
|
|
384
|
+
head_length = max(0.0, float(arrow_length))
|
|
385
|
+
|
|
386
|
+
if length <= 1e-12:
|
|
387
|
+
half = max(half_line, head_length / 3.0)
|
|
388
|
+
point_box = (tip[0] - half, tip[1] - half, tip[0] + half, tip[1] + half)
|
|
389
|
+
return _boxes_overlap(point_box, box)
|
|
390
|
+
|
|
391
|
+
ux, uy = dx / length, dy / length
|
|
392
|
+
vx, vy = -uy, ux
|
|
393
|
+
|
|
394
|
+
if half_line > 0.0:
|
|
395
|
+
shaft = (
|
|
396
|
+
(tip[0] + vx * half_line, tip[1] + vy * half_line),
|
|
397
|
+
(elbow[0] + vx * half_line, elbow[1] + vy * half_line),
|
|
398
|
+
(elbow[0] - vx * half_line, elbow[1] - vy * half_line),
|
|
399
|
+
(tip[0] - vx * half_line, tip[1] - vy * half_line),
|
|
400
|
+
)
|
|
401
|
+
if _convex_polygon_overlaps_box(shaft, box):
|
|
402
|
+
return True
|
|
403
|
+
|
|
404
|
+
if head_length > 0.0:
|
|
405
|
+
half_head = head_length / 3.0
|
|
406
|
+
base_x, base_y = tip[0] + ux * head_length, tip[1] + uy * head_length
|
|
407
|
+
arrow = (
|
|
408
|
+
(tip[0], tip[1]),
|
|
409
|
+
(base_x + vx * half_head, base_y + vy * half_head),
|
|
410
|
+
(base_x - vx * half_head, base_y - vy * half_head),
|
|
411
|
+
)
|
|
412
|
+
if _convex_polygon_overlaps_box(arrow, box):
|
|
413
|
+
return True
|
|
414
|
+
|
|
415
|
+
return False
|
|
416
|
+
|
|
417
|
+
|
|
330
418
|
def _segments_cross_or_overlap(a1, a2, b1, b2) -> bool:
|
|
331
419
|
"""Whether two page-plane segments cross or overlap beyond a shared endpoint.
|
|
332
420
|
|
|
@@ -17,6 +17,14 @@ from collections.abc import Callable
|
|
|
17
17
|
from dataclasses import dataclass, replace
|
|
18
18
|
from typing import cast
|
|
19
19
|
|
|
20
|
+
from b123d_recognisers import (
|
|
21
|
+
RecognitionResult,
|
|
22
|
+
TurnedProfile,
|
|
23
|
+
TurnedStep,
|
|
24
|
+
analyse_cylinders,
|
|
25
|
+
build_recognition_result,
|
|
26
|
+
full_cylinders,
|
|
27
|
+
)
|
|
20
28
|
from build123d import Compound, Shape
|
|
21
29
|
from build123d_drafting.helpers import draft_preset
|
|
22
30
|
from OCP.IFSelect import IFSelect_ReturnStatus
|
|
@@ -47,14 +55,6 @@ from draftwright.compose import (
|
|
|
47
55
|
from draftwright.model import build_part_model
|
|
48
56
|
from draftwright.model.ir import Datum, PartModel, StepFeature, StepLevelFeature
|
|
49
57
|
from draftwright.model.planner import plan_dimensions
|
|
50
|
-
from draftwright.recognition import (
|
|
51
|
-
RecognitionResult,
|
|
52
|
-
TurnedProfile,
|
|
53
|
-
TurnedStep,
|
|
54
|
-
analyse_cylinders,
|
|
55
|
-
build_recognition_result,
|
|
56
|
-
full_cylinders,
|
|
57
|
-
)
|
|
58
58
|
|
|
59
59
|
_log = logging.getLogger(__name__)
|
|
60
60
|
|
|
@@ -1881,6 +1881,7 @@ def render_flats(dwg, plan, a, *, ctx, only=None) -> int:
|
|
|
1881
1881
|
collapse.setdefault(
|
|
1882
1882
|
(
|
|
1883
1883
|
g.facts.axis,
|
|
1884
|
+
g.facts.presentation_axis,
|
|
1884
1885
|
g.facts.axis_direction,
|
|
1885
1886
|
g.facts.axis_line,
|
|
1886
1887
|
g.facts.stock_span,
|
|
@@ -1889,7 +1890,7 @@ def render_flats(dwg, plan, a, *, ctx, only=None) -> int:
|
|
|
1889
1890
|
[],
|
|
1890
1891
|
).append((g, pd))
|
|
1891
1892
|
jobs = []
|
|
1892
|
-
for gi, ((
|
|
1893
|
+
for gi, ((_axis, presentation_axis, _direction, _line, _span, _across), members) in enumerate(
|
|
1893
1894
|
sorted(collapse.items())
|
|
1894
1895
|
):
|
|
1895
1896
|
if only is not None:
|
|
@@ -1920,7 +1921,7 @@ def render_flats(dwg, plan, a, *, ctx, only=None) -> int:
|
|
|
1920
1921
|
)
|
|
1921
1922
|
jobs.append(
|
|
1922
1923
|
(
|
|
1923
|
-
f"m_flat_{
|
|
1924
|
+
f"m_flat_{presentation_axis}{gi}",
|
|
1924
1925
|
view,
|
|
1925
1926
|
vb,
|
|
1926
1927
|
_flat_label(members[0][1].value_text, _tol_suffix(tol, draft)),
|
|
@@ -33,7 +33,7 @@ from draftwright._core import (
|
|
|
33
33
|
_iso_bbox,
|
|
34
34
|
_log,
|
|
35
35
|
)
|
|
36
|
-
from draftwright._geometry import plane_axes
|
|
36
|
+
from draftwright._geometry import _leader_ink_crosses_box, plane_axes
|
|
37
37
|
from draftwright.annotations._common import (
|
|
38
38
|
CROSSABLE_TYPES,
|
|
39
39
|
CorridorCandidate,
|
|
@@ -42,7 +42,6 @@ from draftwright.annotations._common import (
|
|
|
42
42
|
_geom_box,
|
|
43
43
|
_hole_location_coverage_fact,
|
|
44
44
|
_same_location_ordinate,
|
|
45
|
-
_segment_crosses_box,
|
|
46
45
|
_with_hole_center_coverage,
|
|
47
46
|
_with_hole_location_coverage,
|
|
48
47
|
box_within_page_and_clear,
|
|
@@ -1885,15 +1884,25 @@ def _needs_section(feat: HoleFeature | PatternFeature):
|
|
|
1885
1884
|
return bore.cbore is not None or bore.spotface is not None or not bore.through
|
|
1886
1885
|
|
|
1887
1886
|
|
|
1888
|
-
def _leader_hits(leader, tip, elbow, side, obstacles):
|
|
1887
|
+
def _leader_hits(leader, tip, elbow, side, obstacles, draft):
|
|
1889
1888
|
"""True when *leader*'s rendered footprint truly overlaps any *obstacles* box — split into
|
|
1890
|
-
the diagonal tip→elbow SHAFT (checked
|
|
1891
|
-
over-claims the empty triangle it doesn't occupy — #305) and
|
|
1892
|
-
(axis-aligned, so the coarse AABB check there is already exact).
|
|
1889
|
+
the diagonal tip→elbow SHAFT + ARROWHEAD (checked against their analytical rendered ink,
|
|
1890
|
+
since the composite AABB over-claims the empty triangle it doesn't occupy — #305/#367) and
|
|
1891
|
+
the elbow→label shelf+text (axis-aligned, so the coarse AABB check there is already exact).
|
|
1892
|
+
Promoted (#638; pure)."""
|
|
1893
1893
|
full_box = _geom_box(leader)
|
|
1894
1894
|
if full_box is None or not _box_hits(full_box, obstacles):
|
|
1895
1895
|
return False # fast reject: nowhere near any obstacle
|
|
1896
|
-
if any(
|
|
1896
|
+
if any(
|
|
1897
|
+
_leader_ink_crosses_box(
|
|
1898
|
+
tip,
|
|
1899
|
+
elbow,
|
|
1900
|
+
obstacle,
|
|
1901
|
+
arrow_length=draft.arrow_length,
|
|
1902
|
+
line_width=draft.line_width,
|
|
1903
|
+
)
|
|
1904
|
+
for obstacle in obstacles
|
|
1905
|
+
):
|
|
1897
1906
|
return True
|
|
1898
1907
|
label_box = (
|
|
1899
1908
|
(elbow[0], full_box[1], full_box[2], full_box[3])
|
|
@@ -2433,7 +2442,7 @@ def _place_queue(
|
|
|
2433
2442
|
continue
|
|
2434
2443
|
y = final_y[tid]
|
|
2435
2444
|
leader, tip, elbow = _build_leader_at(s, edge, side, y, to_page, elbow_dx, draft, a.SCALE)
|
|
2436
|
-
if _leader_hits(leader, tip, elbow, side, occupied):
|
|
2445
|
+
if _leader_hits(leader, tip, elbow, side, occupied, draft):
|
|
2437
2446
|
crossing.append((s, y, leader))
|
|
2438
2447
|
else:
|
|
2439
2448
|
placed.append((s, y, leader))
|
|
@@ -20,7 +20,7 @@ from dataclasses import field as dataclasses_field
|
|
|
20
20
|
from typing import TYPE_CHECKING, Any, NamedTuple
|
|
21
21
|
|
|
22
22
|
if TYPE_CHECKING:
|
|
23
|
-
from
|
|
23
|
+
from b123d_recognisers import RecognitionResult
|
|
24
24
|
|
|
25
25
|
# PEP 702 @deprecated. A `sys.version_info` guard (not try/except) so the type checker,
|
|
26
26
|
# which targets the 3.10 floor, resolves the backport branch instead of `warnings.deprecated`
|
|
@@ -30,6 +30,7 @@ if sys.version_info >= (3, 13):
|
|
|
30
30
|
else:
|
|
31
31
|
from typing_extensions import deprecated
|
|
32
32
|
|
|
33
|
+
from b123d_recognisers import analyse_cylinders
|
|
33
34
|
from build123d import (
|
|
34
35
|
Color,
|
|
35
36
|
ExportSVG,
|
|
@@ -98,7 +99,7 @@ from draftwright.linting.quality import quality_components
|
|
|
98
99
|
from draftwright.projection import (
|
|
99
100
|
project_view_geometry,
|
|
100
101
|
)
|
|
101
|
-
from draftwright.
|
|
102
|
+
from draftwright.recognition_cache import RecognitionCache
|
|
102
103
|
from draftwright.registry import AnnotationRegistry
|
|
103
104
|
from draftwright.repair import repair_drawing
|
|
104
105
|
|
|
@@ -320,7 +321,7 @@ class BuildState:
|
|
|
320
321
|
"""
|
|
321
322
|
|
|
322
323
|
analysis: Analysis | None = None
|
|
323
|
-
|
|
324
|
+
recognition_cache: RecognitionCache = dataclasses_field(default_factory=RecognitionCache)
|
|
324
325
|
part_model: object | None = None
|
|
325
326
|
view_edge_cache: dict = dataclasses_field(default_factory=dict)
|
|
326
327
|
ann_box_cache: dict = dataclasses_field(default_factory=dict)
|
|
@@ -335,6 +336,16 @@ class BuildState:
|
|
|
335
336
|
#: suppression rule produced four issue reports before anyone found the rule (#997).
|
|
336
337
|
omissions: tuple = ()
|
|
337
338
|
|
|
339
|
+
@property
|
|
340
|
+
def recognition(self) -> RecognitionResult | None:
|
|
341
|
+
"""The immutable result held by Draftwright's consumer-owned lifecycle cache."""
|
|
342
|
+
|
|
343
|
+
return self.recognition_cache.result
|
|
344
|
+
|
|
345
|
+
@recognition.setter
|
|
346
|
+
def recognition(self, value: RecognitionResult | None) -> None:
|
|
347
|
+
self.recognition_cache.result = value
|
|
348
|
+
|
|
338
349
|
def clear_geometry_caches(self) -> None:
|
|
339
350
|
"""The one invalidation seam (finalize rollback): view edges + annotation
|
|
340
351
|
boxes together — a rolled-back drawing must re-measure everything."""
|
|
@@ -353,9 +364,7 @@ class BuildState:
|
|
|
353
364
|
On a detected build ``recognition`` is already filled by the builder, so this returns
|
|
354
365
|
it and recognises nothing.
|
|
355
366
|
"""
|
|
356
|
-
|
|
357
|
-
self.recognition = build_recognition_result(part)
|
|
358
|
-
return self.recognition
|
|
367
|
+
return self.recognition_cache.ensure(part)
|
|
359
368
|
|
|
360
369
|
|
|
361
370
|
class Drawing:
|
|
@@ -5,8 +5,9 @@ from __future__ import annotations
|
|
|
5
5
|
from dataclasses import dataclass
|
|
6
6
|
from typing import Literal
|
|
7
7
|
|
|
8
|
+
from b123d_recognisers import RecognitionResult, has_multi_axis_plates
|
|
9
|
+
|
|
8
10
|
from draftwright.linting.issues import LintIssue
|
|
9
|
-
from draftwright.recognition import RecognitionResult, has_multi_axis_plates
|
|
10
11
|
|
|
11
12
|
ChannelRequirementState = Literal["placed", "suppressed", "dropped", "missing", "unverifiable"]
|
|
12
13
|
|
|
@@ -24,20 +24,7 @@ from collections.abc import Iterable
|
|
|
24
24
|
from math import atan2, pi, tau
|
|
25
25
|
from typing import Literal
|
|
26
26
|
|
|
27
|
-
from
|
|
28
|
-
from build123d_drafting.helpers import CenterMark, Dimension, TitleBlock
|
|
29
|
-
|
|
30
|
-
from draftwright._core import (
|
|
31
|
-
_END_ON,
|
|
32
|
-
HoleRef,
|
|
33
|
-
_annotation_diameter_sources,
|
|
34
|
-
_axis_letter,
|
|
35
|
-
_fmt,
|
|
36
|
-
_xyz,
|
|
37
|
-
)
|
|
38
|
-
from draftwright.linting.issues import LintIssue
|
|
39
|
-
from draftwright.linting.profiled_bore_coverage import profiled_bore_key
|
|
40
|
-
from draftwright.recognition import (
|
|
27
|
+
from b123d_recognisers import (
|
|
41
28
|
RecognitionResult,
|
|
42
29
|
TurnedProfile,
|
|
43
30
|
analyse_cylinders,
|
|
@@ -51,11 +38,24 @@ from draftwright.recognition import (
|
|
|
51
38
|
recognise_rectangular_pads,
|
|
52
39
|
recognise_turned_steps,
|
|
53
40
|
)
|
|
54
|
-
from
|
|
41
|
+
from b123d_recognisers.profiled_bores import (
|
|
55
42
|
double_d_bores_from_openings,
|
|
56
43
|
double_d_profile,
|
|
57
44
|
principal_boundary_plane,
|
|
58
45
|
)
|
|
46
|
+
from build123d import GeomType
|
|
47
|
+
from build123d_drafting.helpers import CenterMark, Dimension, TitleBlock
|
|
48
|
+
|
|
49
|
+
from draftwright._core import (
|
|
50
|
+
_END_ON,
|
|
51
|
+
HoleRef,
|
|
52
|
+
_annotation_diameter_sources,
|
|
53
|
+
_axis_letter,
|
|
54
|
+
_fmt,
|
|
55
|
+
_xyz,
|
|
56
|
+
)
|
|
57
|
+
from draftwright.linting.issues import LintIssue
|
|
58
|
+
from draftwright.linting.profiled_bore_coverage import profiled_bore_key
|
|
59
59
|
|
|
60
60
|
_UNSET = object() # sentinel: distinguishes "not supplied" from a valid prof=None
|
|
61
61
|
|
|
@@ -11,9 +11,10 @@ from collections import defaultdict
|
|
|
11
11
|
from dataclasses import dataclass
|
|
12
12
|
from typing import Literal
|
|
13
13
|
|
|
14
|
+
from b123d_recognisers import RecognitionResult
|
|
15
|
+
|
|
14
16
|
from draftwright._geometry import _canonical_axis_direction
|
|
15
17
|
from draftwright.linting.issues import LintIssue
|
|
16
|
-
from draftwright.recognition import RecognitionResult
|
|
17
18
|
|
|
18
19
|
FlatRequirementState = Literal["placed", "suppressed", "dropped", "missing", "unverifiable"]
|
|
19
20
|
|
|
@@ -12,9 +12,10 @@ from dataclasses import dataclass
|
|
|
12
12
|
from math import hypot
|
|
13
13
|
from typing import Literal
|
|
14
14
|
|
|
15
|
+
from b123d_recognisers import HoleSpec, RecognitionResult, countersink_matches_hole
|
|
16
|
+
|
|
15
17
|
from draftwright._geometry import _is_principal_axis
|
|
16
18
|
from draftwright.linting.issues import LintIssue
|
|
17
|
-
from draftwright.recognition import HoleSpec, RecognitionResult, countersink_matches_hole
|
|
18
19
|
|
|
19
20
|
HoleRequirementState = Literal["placed", "suppressed", "dropped", "missing", "unverifiable"]
|
|
20
21
|
HoleSourceKind = Literal["hole", "hole_pattern"]
|
|
@@ -5,8 +5,9 @@ from __future__ import annotations
|
|
|
5
5
|
from dataclasses import dataclass
|
|
6
6
|
from typing import Literal
|
|
7
7
|
|
|
8
|
+
from b123d_recognisers import RecognitionResult
|
|
9
|
+
|
|
8
10
|
from draftwright.linting.issues import LintIssue
|
|
9
|
-
from draftwright.recognition import RecognitionResult
|
|
10
11
|
|
|
11
12
|
PolygonalStockState = Literal["placed", "suppressed", "dropped", "missing", "unverifiable"]
|
|
12
13
|
|
|
@@ -11,9 +11,10 @@ from collections import Counter
|
|
|
11
11
|
from math import hypot, isfinite
|
|
12
12
|
from typing import Literal
|
|
13
13
|
|
|
14
|
+
from b123d_recognisers import RecognitionResult
|
|
15
|
+
|
|
14
16
|
from draftwright._geometry import _fmt
|
|
15
17
|
from draftwright.linting.issues import LintIssue
|
|
16
|
-
from draftwright.recognition import RecognitionResult
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
def _axis_letter(value) -> str:
|
|
@@ -12,8 +12,9 @@ from collections import defaultdict
|
|
|
12
12
|
from dataclasses import dataclass
|
|
13
13
|
from typing import Literal
|
|
14
14
|
|
|
15
|
+
from b123d_recognisers import RecognitionResult
|
|
16
|
+
|
|
15
17
|
from draftwright.linting.issues import LintIssue
|
|
16
|
-
from draftwright.recognition import RecognitionResult
|
|
17
18
|
|
|
18
19
|
SlotRequirementState = Literal["placed", "suppressed", "dropped", "missing", "unverifiable"]
|
|
19
20
|
SlotSourceKind = Literal["slot", "slot_pattern"]
|
|
@@ -272,7 +272,14 @@ _FACTS: dict[str, tuple[str, ...]] = {
|
|
|
272
272
|
# These are STRUCTURE, not measurements: together they say which piece of stock a flat
|
|
273
273
|
# belongs to, so the renderer can tell one double-D's two faces from independent aligned
|
|
274
274
|
# or slanted regions (#1013/#1036). The drawing prints none of them.
|
|
275
|
-
"flat": (
|
|
275
|
+
"flat": (
|
|
276
|
+
"frame",
|
|
277
|
+
"axis",
|
|
278
|
+
"presentation_axis",
|
|
279
|
+
"axis_line",
|
|
280
|
+
"stock_span",
|
|
281
|
+
"axis_direction",
|
|
282
|
+
),
|
|
276
283
|
"groove": ("frame", "axis"),
|
|
277
284
|
"plate": ("frame", "axis"),
|
|
278
285
|
# Raw AP242 PMI is the documented non-generated exception: its source-authored label
|
|
@@ -290,7 +290,7 @@ def double_d_bore(
|
|
|
290
290
|
with exactly that boundary; arbitrary line/arc profiles and blind profiles fail closed.
|
|
291
291
|
"""
|
|
292
292
|
if obj is not None:
|
|
293
|
-
from
|
|
293
|
+
from b123d_recognisers.profiled_bores import read_double_d_tool
|
|
294
294
|
|
|
295
295
|
r_axis, r_major, r_af, r_at, r_depth, r_direction = read_double_d_tool(obj)
|
|
296
296
|
axis = r_axis if axis is None else axis
|
|
@@ -329,12 +329,11 @@ def read_countersink(cone) -> tuple[float, float]:
|
|
|
329
329
|
"""``(major_diameter, included_angle°)`` of a countersink **cone** tool — the larger rim ⌀
|
|
330
330
|
and the full cone angle, read off its conical **face** (not a removed edge, #576 lesson).
|
|
331
331
|
The cone geometry is the recogniser's own
|
|
332
|
-
:func:`~
|
|
332
|
+
:func:`~b123d_recognisers.countersinks.cone_rims` (#704), so a declared
|
|
333
333
|
countersink reads identically to a detected one by construction."""
|
|
334
|
+
from b123d_recognisers import cone_rims
|
|
334
335
|
from build123d import GeomType
|
|
335
336
|
|
|
336
|
-
from draftwright.recognition import cone_rims
|
|
337
|
-
|
|
338
337
|
faces = cone.faces().filter_by(GeomType.CONE)
|
|
339
338
|
if not faces:
|
|
340
339
|
raise ValueError("countersink(cone=...) needs a conical tool (a build123d Cone)")
|
|
@@ -506,10 +505,10 @@ def _read_chamfer_face(face) -> tuple[str, float, float, Point]:
|
|
|
506
505
|
"""Read a chamfer off its **oblique planar bevel face**: the axis (the edge the chamfer
|
|
507
506
|
runs along), the two legs (the face's in-plane extents), and a point **on the bevel** (the
|
|
508
507
|
face centre — not the removed sharp corner). The classification + leg geometry is the
|
|
509
|
-
recogniser's own :func:`~
|
|
508
|
+
recogniser's own :func:`~b123d_recognisers.chamfers.classify_bevel` (#704), so a
|
|
510
509
|
declared chamfer reads identically to a detected one by construction; only the
|
|
511
510
|
user-facing error messages live here."""
|
|
512
|
-
from
|
|
511
|
+
from b123d_recognisers import BevelReject, classify_bevel
|
|
513
512
|
|
|
514
513
|
try:
|
|
515
514
|
edge_i, _nv, _span, hi, lo = classify_bevel(face)
|
|
@@ -597,7 +596,7 @@ def _read_fillet_face(face) -> tuple[str, float, Point]:
|
|
|
597
596
|
# Anchor on the curved radius surface itself — the recogniser's own fillet_anchor
|
|
598
597
|
# (#622 lesson: never the bbox centre), so a declared fillet's leader tip is
|
|
599
598
|
# identical to the detected one's by construction (#704).
|
|
600
|
-
from
|
|
599
|
+
from b123d_recognisers import fillet_anchor
|
|
601
600
|
|
|
602
601
|
p = fillet_anchor(s)
|
|
603
602
|
return (
|
|
@@ -722,7 +721,7 @@ def _read_groove_face(face) -> tuple[str, float, float, Point]:
|
|
|
722
721
|
span = ((bb.min.X, bb.max.X), (bb.min.Y, bb.max.Y), (bb.min.Z, bb.max.Z))[axis_i]
|
|
723
722
|
# The leader tip is the recogniser's own floor_face_anchor (#704), so a declared
|
|
724
723
|
# groove anchors identically to a detected one by construction.
|
|
725
|
-
from
|
|
724
|
+
from b123d_recognisers import floor_face_anchor
|
|
726
725
|
|
|
727
726
|
c = floor_face_anchor(face)
|
|
728
727
|
return (
|
|
@@ -874,7 +873,7 @@ def _read_step_levels(
|
|
|
874
873
|
mirroring ``model/detect.py``), the ``datum`` (bbox min corner the positions measure from)
|
|
875
874
|
and ``at`` (the frame anchor — bbox centre X/Y at ``base``, matching ``detect.py`` so an
|
|
876
875
|
object round-trips to the same IR)."""
|
|
877
|
-
from
|
|
876
|
+
from b123d_recognisers import (
|
|
878
877
|
project_step_shoulders,
|
|
879
878
|
recognise_risers,
|
|
880
879
|
step_level_records,
|