draftwright 0.4.11__tar.gz → 0.4.13__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.11 → draftwright-0.4.13}/PKG-INFO +1 -1
- {draftwright-0.4.11 → draftwright-0.4.13}/pyproject.toml +1 -1
- draftwright-0.4.13/src/draftwright/_build_profile.py +186 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/_geometry.py +9 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/_pmi_part21.py +234 -2
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/annotations/_common.py +43 -2
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/annotations/from_model.py +450 -483
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/annotations/holes.py +5 -3
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/annotations/leaders.py +74 -16
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/builder.py +129 -19
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/drawing.py +2 -1
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/linting/pmi_coverage.py +128 -31
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/linting/quality.py +4 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/model/__init__.py +8 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/model/callout.py +8 -1
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/model/compiled.py +10 -3
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/model/declare.py +69 -5
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/model/detect.py +7 -2
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/model/ir.py +328 -3
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/model/planner.py +9 -2
- draftwright-0.4.13/src/draftwright/model/pmi_lowering.py +839 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/pmi.py +482 -35
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/sheet.py +86 -3
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/sheet_emit.py +212 -33
- draftwright-0.4.13/tests/fixtures/ap242_single_cylinder_diameter.step +163 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_adr0018_view_selection.py +20 -4
- draftwright-0.4.13/tests/test_build_profile_harness.py +194 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_e2e_standards.py +8 -3
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_import_boundaries.py +3 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1058_wheel_profile.py +4 -1
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1143_hole_completeness.py +61 -33
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1144_transactional_hole_table.py +15 -8
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1166_cross_pass_feature_leaders.py +83 -11
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1176_quality_fidelity.py +5 -1
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1177_angular_dimensions.py +3 -3
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1187_unroutable_leaders.py +0 -1
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1204_multiscale_view_issues.py +4 -3
- draftwright-0.4.13/tests/test_issue_1209_linear_pmi_witnesses.py +181 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1215_no_approved_tolerance_is_dropped.py +65 -54
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1216_callout_reservation_measures_ink.py +15 -5
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1276_turned_leader_targets.py +11 -4
- draftwright-0.4.13/tests/test_issue_1296_cylindrical_diameter_pmi.py +839 -0
- draftwright-0.4.13/tests/test_issue_1298_manufacturing_requirements.py +1178 -0
- draftwright-0.4.13/tests/test_issue_1299_page_escalation.py +465 -0
- draftwright-0.4.13/tests/test_issue_1308_machined_leader_analytics.py +253 -0
- draftwright-0.4.13/tests/test_issue_1312_engine_costs.py +25 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_740_leader_assignment.py +46 -36
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_798_silhouette_lint.py +1 -1
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_885_prismatic_coverage.py +4 -1
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_917_open_channel.py +5 -2
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_layout_property.py +4 -4
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_make_drawing.py +14 -20
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_pmi.py +92 -1
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_pmi_part21.py +161 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_private_test_imports.py +6 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_sheet_emit.py +106 -33
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_slanted_blind_step.py +12 -5
- draftwright-0.4.11/src/draftwright/model/pmi_lowering.py +0 -276
- {draftwright-0.4.11 → draftwright-0.4.13}/.gitignore +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/CHANGELOG.md +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/LICENSE +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/README.md +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/docs/adr/README.md +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/docs/reference/recogniser-capabilities.md +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/docs/reference/sheet.md +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/docs/research/1062-repeating-radial-profile-evidence.md +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/skills/SKILL.md +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/__init__.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/_core.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/_warnings.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/analysis.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/annotate.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/annotations/__init__.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/annotations/balloons.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/annotations/gears.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/annotations/orchestrator.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/annotations/sections.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/audit.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/cli.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/compose.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/evaluation/__init__.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/evaluation/step_analysis.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/export.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/fits.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/fonts/IBMPlexMono-Regular.ttf +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/fonts/IBMPlexSansCondensed-Regular.ttf +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/fonts/LICENSE-IBMPlexMono-OFL.txt +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/fonts/LICENSE-IBMPlexSansCondensed-OFL.txt +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/fonts/__init__.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/intents.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/layout.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/linting/__init__.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/linting/channel_coverage.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/linting/coverage.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/linting/evidence.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/linting/flat_coverage.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/linting/gear_coverage.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/linting/hole_coverage.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/linting/issues.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/linting/polygonal_stock_coverage.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/linting/profiled_bore_coverage.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/linting/slot_coverage.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/linting/structural.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/linting/suggest.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/make_drawing.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/projection.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/py.typed +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/recogniser_contract.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/recognition/__init__.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/recognition_cache.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/registry.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/repair.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/score.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/src/draftwright/view_plan.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/_kernel.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/_layout_sig.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/conftest.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/evaluation/README.md +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/evaluation/ambiguous-open-semicircle.step +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/evaluation/blind-hole.step +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/evaluation/corpus-v1.json +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/evaluation/plain-block.step +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/evaluation/topology-a.step +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/evaluation/topology-b.step +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/grm03_thumbwheel_drive_screw.step +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/grm04_drive_plate.step +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/if_step_flat_across_cylinder.step +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/issue_1058_wheel_rh.step +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/issue_909_basic_part_design_017_body.step +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/issue_915_case_study_2.step +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/nist_ctc_01_asme1_ap203.stp +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/nist_ctc_01_asme1_ap242.stp +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/nist_ctc_02_asme1_ap203.stp +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/nist_ctc_02_asme1_ap242.stp +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/nist_ctc_03_asme1_ap203.stp +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/nist_ctc_03_asme1_ap242.stp +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/nist_ctc_04_asme1_ap203.stp +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/nist_ctc_04_asme1_ap242.stp +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/nist_ctc_05_asme1_ap203.stp +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/nist_ctc_05_asme1_ap242.stp +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/fixtures/tuner_jig_blind_obround_pockets.step +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/refactor_golden/bracket_section.json +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/refactor_golden/centered_rebate.json +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/refactor_golden/chamfered.json +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/refactor_golden/filleted.json +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/refactor_golden/flange_dense.json +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/refactor_golden/grid_plate.json +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/refactor_golden/grooved_shaft.json +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/refactor_golden/hex_bar.json +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/refactor_golden/holed_slot.json +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/refactor_golden/pocketed.json +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/refactor_golden/prismatic_ladder.json +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/refactor_golden/scattered_plate.json +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/refactor_golden/side_drilled.json +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/refactor_golden/turned_stepped.json +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_add_dimension.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_adr0018_arrangement_gate.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_adr0018_view_routing.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_agents_guide.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_api_docs.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_architecture_docs.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_audit_differential.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_balloon_ring_standoff.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_carve_free_position_callers.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_compiled_plan_boundary.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_counting_calls.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_declare.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_declared_recognition_gate.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_dense_sheet_canary.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_deprecation_dates.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_detect_once.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_detect_registry.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_dimension_role_vocabulary.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_drawing_encapsulation.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_e2e_slice.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_export_dxf_curves.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_export_dxf_zoom.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_external_recognition_boundary.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_fillets_adjacency.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_fits.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_flat_completeness.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_flat_stock_identity.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_flat_stock_opposition.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_gdt_placement.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_grid_lattice_convention.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_iso_nts_caption_placement.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1000_envelope_reuse.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1073_cross_body_patterns.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1082_polygonal_stock.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1086_declared_gears.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1116_ap242_hole_tolerance_lowering.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1130_view_planning_evidence.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1142_hole_leader_labels.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1146_scale_completeness.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1147_legibility_pair_aggregation.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1153_contradictory_dimensions.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1154_one_owner_per_measurement.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1155_grm04_sheet_use.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1188_per_view_assignment.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1190_section_decision.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1196_deterministic_view_names.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1202_observed_drawing_consumer.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1215_envelope_tolerance.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1216_pairwise_across_scale_groups.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1217_claimed_representations.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1217_the_facility_is_shared.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1219_location_claims_its_own_axis.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1229_ledger_member_keying.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1240_iso_above_strip_visibility.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1254_turned_chamfers.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1260_view_constraints.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_1281_turned_fillets.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_367_leader_ink.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_443_grm03_axial_coverage.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_676_polygonal_boss.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_676_polygonal_boss_declare.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_798_floor_cardinality.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_798_material_field.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_909_sloped_profile.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_915_dense_case.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_916_pocket_leader.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_924_zero_length_shoulders.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_955_height_contingency.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_issue_958_cross_solid_recognition.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_label_provenance.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_layout.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_layout_cleanliness.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_layout_hypothesis.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_leader_footprint.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_lint_box_cache.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_lint_reconciliation.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_lint_structural.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_linting.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_location_vocabulary.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_note_verb.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_object_aspects.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_occupancy_boxes.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_parameter_id.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_part_model.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_pitch_dim_footprint.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_pmi_datum_lowering.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_pmi_gtol_lowering.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_pocket_pattern.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_pocket_pattern_recognition.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_principal_profile_classifier.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_private_test_attr_reads.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_quality_components.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_recogniser_adoption.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_recogniser_capabilities.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_recogniser_contract.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_recognition.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_recognition_manifest.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_recognition_result.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_refactor_golden.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_registry.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_render_seam.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_repack_geometry_seam.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_score.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_script_detail_parity.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_shared_box_memo.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_sheet_gdt.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_sheet_identity_invariant.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_sheet_notes.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_sheet_of.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_sheet_section.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_sheet_tables.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_slot_completeness.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_slot_pattern.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_slot_pattern_recognition.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_soft_deprecation.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_solve_trace.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_step_analysis_evaluation.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_strip_layout.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_suppression_ledger.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_suppression_marks.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_tolerances.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_turned_steps.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_view_plan.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_witness_label_reconciliation.py +0 -0
- {draftwright-0.4.11 → draftwright-0.4.13}/tests/test_workflows.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: draftwright
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.13
|
|
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
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""Pytest plugin and runner support for reproducible build-cost profiles (#1307)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
import sys
|
|
8
|
+
from collections import defaultdict
|
|
9
|
+
from collections.abc import Callable
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from functools import wraps
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from time import perf_counter
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
_MIN_REPORT_SECONDS = 0.005
|
|
17
|
+
_STATE: _ProfileState | None = None
|
|
18
|
+
_CURRENT_NODEID = "<collection>"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class _ProfileState:
|
|
23
|
+
output_dir: Path
|
|
24
|
+
worker_id: str
|
|
25
|
+
collected: int | None = None
|
|
26
|
+
calls: dict[tuple[str, str], list[float]] = field(
|
|
27
|
+
default_factory=lambda: defaultdict(lambda: [0, 0.0])
|
|
28
|
+
)
|
|
29
|
+
phases: list[dict[str, Any]] = field(default_factory=list)
|
|
30
|
+
|
|
31
|
+
def record_call(self, seam: str, seconds: float) -> None:
|
|
32
|
+
aggregate = self.calls[(_CURRENT_NODEID, seam)]
|
|
33
|
+
aggregate[0] += 1
|
|
34
|
+
aggregate[1] += seconds
|
|
35
|
+
|
|
36
|
+
def write(self, exitstatus: int) -> Path:
|
|
37
|
+
self.output_dir.mkdir(parents=True, exist_ok=True)
|
|
38
|
+
safe_worker = re.sub(r"[^A-Za-z0-9_.-]", "_", self.worker_id)
|
|
39
|
+
path = self.output_dir / f"profile-{safe_worker}.json"
|
|
40
|
+
payload = {
|
|
41
|
+
"worker_id": self.worker_id,
|
|
42
|
+
"collected": self.collected,
|
|
43
|
+
"exitstatus": exitstatus,
|
|
44
|
+
"calls": [
|
|
45
|
+
{
|
|
46
|
+
"nodeid": nodeid,
|
|
47
|
+
"seam": seam,
|
|
48
|
+
"count": int(values[0]),
|
|
49
|
+
"seconds": values[1],
|
|
50
|
+
}
|
|
51
|
+
for (nodeid, seam), values in sorted(self.calls.items())
|
|
52
|
+
],
|
|
53
|
+
"phases": self.phases,
|
|
54
|
+
}
|
|
55
|
+
path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
56
|
+
return path
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _timed(seam: str, function: Callable[..., Any]) -> Callable[..., Any]:
|
|
60
|
+
@wraps(function)
|
|
61
|
+
def wrapper(*args, **kwargs):
|
|
62
|
+
started = perf_counter()
|
|
63
|
+
try:
|
|
64
|
+
return function(*args, **kwargs)
|
|
65
|
+
finally:
|
|
66
|
+
assert _STATE is not None
|
|
67
|
+
_STATE.record_call(seam, perf_counter() - started)
|
|
68
|
+
|
|
69
|
+
return wrapper
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def pytest_addoption(parser) -> None:
|
|
73
|
+
group = parser.getgroup("draftwright-build-profile")
|
|
74
|
+
group.addoption(
|
|
75
|
+
"--build-profile-dir",
|
|
76
|
+
help="write one Draftwright build profile JSON file per pytest-xdist worker",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def pytest_configure(config) -> None:
|
|
81
|
+
global _STATE
|
|
82
|
+
output = config.getoption("--build-profile-dir")
|
|
83
|
+
if not output:
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
worker_input = getattr(config, "workerinput", None)
|
|
87
|
+
# Under xdist the controller executes no tests; worker processes write the useful files.
|
|
88
|
+
if worker_input is None and getattr(config.option, "numprocesses", None):
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
worker_id = worker_input.get("workerid", "worker") if worker_input else "main"
|
|
92
|
+
_STATE = _ProfileState(Path(output).resolve(), worker_id)
|
|
93
|
+
|
|
94
|
+
import draftwright.builder as builder
|
|
95
|
+
import draftwright.sheet as sheet
|
|
96
|
+
|
|
97
|
+
draftwright = sys.modules["draftwright"]
|
|
98
|
+
builder_public = _timed("builder.build_drawing", builder.build_drawing)
|
|
99
|
+
builder.build_drawing = builder_public
|
|
100
|
+
# The public lazy package attribute and Sheet's import-time binding otherwise bypass a
|
|
101
|
+
# patch installed only on builder.py. That omission made the first #1307 census a lower
|
|
102
|
+
# bound, so all three bindings are explicit here.
|
|
103
|
+
setattr(draftwright, "build_drawing", builder_public)
|
|
104
|
+
sheet.build_drawing = _timed("sheet.build_drawing", sheet.build_drawing)
|
|
105
|
+
builder._build_drawing_once = _timed(
|
|
106
|
+
"builder._build_drawing_once", builder._build_drawing_once
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def pytest_collection_finish(session) -> None:
|
|
111
|
+
if _STATE is not None:
|
|
112
|
+
_STATE.collected = len(session.items)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def pytest_runtest_logstart(nodeid, location) -> None:
|
|
116
|
+
del location
|
|
117
|
+
global _CURRENT_NODEID
|
|
118
|
+
_CURRENT_NODEID = nodeid
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def pytest_runtest_logfinish(nodeid, location) -> None:
|
|
122
|
+
del nodeid, location
|
|
123
|
+
global _CURRENT_NODEID
|
|
124
|
+
_CURRENT_NODEID = "<between-tests>"
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def pytest_runtest_logreport(report) -> None:
|
|
128
|
+
if _STATE is not None and report.duration >= _MIN_REPORT_SECONDS:
|
|
129
|
+
_STATE.phases.append(
|
|
130
|
+
{
|
|
131
|
+
"nodeid": report.nodeid,
|
|
132
|
+
"phase": report.when,
|
|
133
|
+
"seconds": report.duration,
|
|
134
|
+
"outcome": report.outcome,
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def pytest_sessionfinish(session, exitstatus) -> None:
|
|
140
|
+
del session
|
|
141
|
+
if _STATE is not None:
|
|
142
|
+
_STATE.write(int(exitstatus))
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def build_pytest_command(output_dir: Path, pytest_args: list[str]) -> list[str]:
|
|
146
|
+
"""Return argv without joining *pytest_args* into a shell-expanded module string."""
|
|
147
|
+
return [
|
|
148
|
+
"uv",
|
|
149
|
+
"run",
|
|
150
|
+
"pytest",
|
|
151
|
+
"-p",
|
|
152
|
+
"draftwright._build_profile",
|
|
153
|
+
f"--build-profile-dir={output_dir}",
|
|
154
|
+
*pytest_args,
|
|
155
|
+
]
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def summarise_profiles(output_dir: Path, expected_collected: int) -> dict[str, Any]:
|
|
159
|
+
paths = sorted(output_dir.glob("profile-*.json"))
|
|
160
|
+
if not paths:
|
|
161
|
+
raise ValueError(f"no worker profiles were written to {output_dir}")
|
|
162
|
+
|
|
163
|
+
by_seam: dict[str, list[float]] = defaultdict(lambda: [0, 0.0])
|
|
164
|
+
workers = []
|
|
165
|
+
for path in paths:
|
|
166
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
167
|
+
collected = payload.get("collected")
|
|
168
|
+
if collected != expected_collected:
|
|
169
|
+
raise ValueError(
|
|
170
|
+
f"{path.name} collected {collected!r}, expected {expected_collected}; "
|
|
171
|
+
"refusing to report a successful empty or partial profile"
|
|
172
|
+
)
|
|
173
|
+
workers.append(payload["worker_id"])
|
|
174
|
+
for call in payload["calls"]:
|
|
175
|
+
aggregate = by_seam[call["seam"]]
|
|
176
|
+
aggregate[0] += call["count"]
|
|
177
|
+
aggregate[1] += call["seconds"]
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
"expected_collected": expected_collected,
|
|
181
|
+
"workers": workers,
|
|
182
|
+
"seams": {
|
|
183
|
+
seam: {"count": int(values[0]), "seconds": values[1]}
|
|
184
|
+
for seam, values in sorted(by_seam.items())
|
|
185
|
+
},
|
|
186
|
+
}
|
|
@@ -456,6 +456,15 @@ def _convex_polygons_overlap(left, right) -> bool:
|
|
|
456
456
|
|
|
457
457
|
if len(left) < 3 or len(right) < 3:
|
|
458
458
|
return False
|
|
459
|
+
left_x, left_y = zip(*left, strict=True)
|
|
460
|
+
right_x, right_y = zip(*right, strict=True)
|
|
461
|
+
if (
|
|
462
|
+
max(left_x) <= min(right_x)
|
|
463
|
+
or max(right_x) <= min(left_x)
|
|
464
|
+
or max(left_y) <= min(right_y)
|
|
465
|
+
or max(right_y) <= min(left_y)
|
|
466
|
+
):
|
|
467
|
+
return False
|
|
459
468
|
axes: list[tuple[float, float]] = []
|
|
460
469
|
for points in (left, right):
|
|
461
470
|
axes.extend(
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
"""Structured Part21 facts that OCCT's XCAF PMI transfer currently drops.
|
|
2
2
|
|
|
3
3
|
This is deliberately a leaf adapter: it understands ISO 10303-21 entities and units, but
|
|
4
|
-
knows nothing about XCAF labels, drafting IR, or placement. ``pmi.py`` owns the overlay
|
|
5
|
-
|
|
4
|
+
knows nothing about XCAF labels, drafting IR, or placement. ``pmi.py`` owns the overlay.
|
|
5
|
+
Geometric-tolerance facts require exact XCAF correspondence; manufacturing-requirement facts
|
|
6
|
+
retain their own complete property -> representation -> shape-aspect source chain.
|
|
6
7
|
"""
|
|
7
8
|
|
|
8
9
|
from __future__ import annotations
|
|
9
10
|
|
|
10
11
|
import math
|
|
12
|
+
import re
|
|
11
13
|
from dataclasses import dataclass
|
|
12
14
|
from pathlib import Path
|
|
13
15
|
|
|
@@ -53,6 +55,13 @@ _SI_METRE_TO_MM: dict[str, float] = {
|
|
|
53
55
|
".ATTO.": 1e-15,
|
|
54
56
|
}
|
|
55
57
|
|
|
58
|
+
# Entity names are invariant ASCII Part21 syntax; string parameters are not. Keying this
|
|
59
|
+
# guard on the literal category text silently missed valid ``\X2\...\X0\`` encodings before
|
|
60
|
+
# steputils had a chance to decode them. A file with no property definitions cannot carry
|
|
61
|
+
# the requirement chain, while any file that does is parsed conservatively (#1297). Match
|
|
62
|
+
# only the invariant token: Part 21 comments may legally separate it from the opening ``(``.
|
|
63
|
+
_PROPERTY_DEFINITION_MARKER = re.compile(rb"\bPROPERTY_DEFINITION\b", re.IGNORECASE)
|
|
64
|
+
|
|
56
65
|
|
|
57
66
|
@dataclass(frozen=True)
|
|
58
67
|
class GeometricToleranceFact:
|
|
@@ -84,7 +93,31 @@ class DatumOccurrenceFact:
|
|
|
84
93
|
reason: str = ""
|
|
85
94
|
|
|
86
95
|
|
|
96
|
+
@dataclass(frozen=True)
|
|
97
|
+
class ManufacturingRequirementFact:
|
|
98
|
+
"""One authoritative semantic manufacturing requirement from Part 21.
|
|
99
|
+
|
|
100
|
+
``entity_id`` is the ``PROPERTY_DEFINITION`` identity, while ``text`` comes from the
|
|
101
|
+
linked ``DESCRIPTIVE_REPRESENTATION_ITEM`` rather than presentation glyphs. The
|
|
102
|
+
remaining IDs preserve the semantic-callout -> shape-aspect -> representation-item
|
|
103
|
+
association for concept lowering; this leaf adapter deliberately does not interpret
|
|
104
|
+
those items as drafting features.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
entity_id: str
|
|
108
|
+
semantic_name: str
|
|
109
|
+
text: str
|
|
110
|
+
representation_id: str = ""
|
|
111
|
+
descriptive_item_id: str = ""
|
|
112
|
+
callout_ids: tuple[str, ...] = ()
|
|
113
|
+
shape_aspect_ids: tuple[str, ...] = ()
|
|
114
|
+
reference_item_ids: tuple[str, ...] = ()
|
|
115
|
+
reason: str = ""
|
|
116
|
+
|
|
117
|
+
|
|
87
118
|
def _entities(instance) -> tuple:
|
|
119
|
+
if instance is None:
|
|
120
|
+
return ()
|
|
88
121
|
entity = getattr(instance, "entity", None)
|
|
89
122
|
if entity is not None:
|
|
90
123
|
return (entity,)
|
|
@@ -158,6 +191,205 @@ def _datum_feature_items(step) -> dict[str, tuple[str, ...]]:
|
|
|
158
191
|
return {key: tuple(dict.fromkeys(values)) for key, values in result.items()}
|
|
159
192
|
|
|
160
193
|
|
|
194
|
+
def _text(value) -> str:
|
|
195
|
+
return str(value).strip() if isinstance(value, str) else ""
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _name_tokens(value: str) -> frozenset[str]:
|
|
199
|
+
"""Normalise semantic names for callout correspondence, not requirement inference."""
|
|
200
|
+
words = []
|
|
201
|
+
for raw in value.lower().replace("_", " ").replace("-", " ").split():
|
|
202
|
+
word = "".join(character for character in raw if character.isalnum())
|
|
203
|
+
if len(word) > 3 and word.endswith("s") and not word.endswith("ss"):
|
|
204
|
+
word = word[:-1]
|
|
205
|
+
if word and word != "requirement":
|
|
206
|
+
words.append(word)
|
|
207
|
+
return frozenset(words)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _manufacturing_graph(step):
|
|
211
|
+
"""Collect the Part21 indexes shared by manufacturing-requirement facts."""
|
|
212
|
+
definitions: list[tuple[str, object]] = []
|
|
213
|
+
representations: dict[str, list[str]] = {}
|
|
214
|
+
callout_names: dict[str, str] = {}
|
|
215
|
+
callout_aspects: dict[str, list[str]] = {}
|
|
216
|
+
aspect_items: dict[str, list[str]] = {}
|
|
217
|
+
|
|
218
|
+
for section in step.data:
|
|
219
|
+
for entity_id, instance in section.instances.items():
|
|
220
|
+
definition = _entity_named(instance, "PROPERTY_DEFINITION")
|
|
221
|
+
if (
|
|
222
|
+
definition is not None
|
|
223
|
+
and len(definition.params) >= 2
|
|
224
|
+
and _text(definition.params[0]).lower() == "manufacturing requirement"
|
|
225
|
+
):
|
|
226
|
+
definitions.append((entity_id, definition))
|
|
227
|
+
|
|
228
|
+
relationship = _entity_named(instance, "PROPERTY_DEFINITION_REPRESENTATION")
|
|
229
|
+
if relationship is not None and len(relationship.params) >= 2:
|
|
230
|
+
definition_ref, representation_ref = relationship.params[:2]
|
|
231
|
+
if isinstance(definition_ref, p21.Reference) and isinstance(
|
|
232
|
+
representation_ref, p21.Reference
|
|
233
|
+
):
|
|
234
|
+
representations.setdefault(str(definition_ref), []).append(
|
|
235
|
+
str(representation_ref)
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
callout = _entity_named(instance, "DRAUGHTING_CALLOUT")
|
|
239
|
+
if callout is not None and callout.params:
|
|
240
|
+
callout_names[entity_id] = _text(callout.params[0])
|
|
241
|
+
|
|
242
|
+
association = _entity_named(instance, "DRAUGHTING_MODEL_ITEM_ASSOCIATION")
|
|
243
|
+
if association is not None and len(association.params) >= 5:
|
|
244
|
+
aspect_ref = association.params[2]
|
|
245
|
+
if isinstance(aspect_ref, p21.Reference) and _instance_is(
|
|
246
|
+
step, str(aspect_ref), "SHAPE_ASPECT"
|
|
247
|
+
):
|
|
248
|
+
for callout_ref in _references(association.params[4]):
|
|
249
|
+
if _instance_is(step, callout_ref, "DRAUGHTING_CALLOUT"):
|
|
250
|
+
callout_aspects.setdefault(callout_ref, []).append(str(aspect_ref))
|
|
251
|
+
|
|
252
|
+
usage = _entity_named(instance, "GEOMETRIC_ITEM_SPECIFIC_USAGE")
|
|
253
|
+
if usage is not None and len(usage.params) >= 5:
|
|
254
|
+
aspect_ref = usage.params[2]
|
|
255
|
+
if isinstance(aspect_ref, p21.Reference) and _instance_is(
|
|
256
|
+
step, str(aspect_ref), "SHAPE_ASPECT"
|
|
257
|
+
):
|
|
258
|
+
aspect_items.setdefault(str(aspect_ref), []).extend(
|
|
259
|
+
_references(usage.params[4])
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
return definitions, representations, callout_names, callout_aspects, aspect_items
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def read_manufacturing_requirements(
|
|
266
|
+
step_file: str | Path,
|
|
267
|
+
) -> tuple[ManufacturingRequirementFact, ...]:
|
|
268
|
+
"""Read authoritative semantic requirements and retain their geometry associations.
|
|
269
|
+
|
|
270
|
+
The property-definition chain owns the full requirement text. A draughting callout
|
|
271
|
+
with the same representation name (or, where the presentation adds a qualifier such as
|
|
272
|
+
``Straight``, the same semantic-name tokens) supplies the shape-aspect association.
|
|
273
|
+
This is correspondence between two source semantic identities; no nominal value or
|
|
274
|
+
B-rep measurement is guessed here.
|
|
275
|
+
"""
|
|
276
|
+
# A file with no property-definition entity cannot carry this semantic chain, so avoid a
|
|
277
|
+
# third full Part21 parse there. Once any property definition exists, parse
|
|
278
|
+
# conservatively: its string category may use a valid escape encoding that a byte-level
|
|
279
|
+
# literal check cannot interpret.
|
|
280
|
+
if _PROPERTY_DEFINITION_MARKER.search(Path(step_file).read_bytes()) is None:
|
|
281
|
+
return ()
|
|
282
|
+
step = p21.readfile(step_file)
|
|
283
|
+
(
|
|
284
|
+
definitions,
|
|
285
|
+
representations,
|
|
286
|
+
callout_names,
|
|
287
|
+
callout_aspects,
|
|
288
|
+
aspect_items,
|
|
289
|
+
) = _manufacturing_graph(step)
|
|
290
|
+
facts: list[ManufacturingRequirementFact] = []
|
|
291
|
+
|
|
292
|
+
for entity_id, definition in definitions:
|
|
293
|
+
semantic_name = _text(definition.params[1])
|
|
294
|
+
reasons: list[str] = []
|
|
295
|
+
if not semantic_name:
|
|
296
|
+
reasons.append("manufacturing requirement has no semantic name")
|
|
297
|
+
|
|
298
|
+
representation_ids = tuple(dict.fromkeys(representations.get(entity_id, ())))
|
|
299
|
+
representation_id = representation_ids[0] if len(representation_ids) == 1 else ""
|
|
300
|
+
if len(representation_ids) != 1:
|
|
301
|
+
reasons.append(
|
|
302
|
+
f"manufacturing requirement has {len(representation_ids)} linked representations"
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
representation_name = ""
|
|
306
|
+
descriptive_ids: tuple[str, ...] = ()
|
|
307
|
+
if representation_id:
|
|
308
|
+
representation = _entity_named(step.get(representation_id), "REPRESENTATION")
|
|
309
|
+
if representation is None or len(representation.params) < 2:
|
|
310
|
+
reasons.append(
|
|
311
|
+
f"linked representation {representation_id} is unavailable or malformed"
|
|
312
|
+
)
|
|
313
|
+
else:
|
|
314
|
+
representation_name = _text(representation.params[0])
|
|
315
|
+
descriptive_ids = tuple(
|
|
316
|
+
dict.fromkeys(
|
|
317
|
+
ref
|
|
318
|
+
for ref in _references(representation.params[1])
|
|
319
|
+
if _instance_is(step, ref, "DESCRIPTIVE_REPRESENTATION_ITEM")
|
|
320
|
+
)
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
descriptive_item_id = descriptive_ids[0] if len(descriptive_ids) == 1 else ""
|
|
324
|
+
text = ""
|
|
325
|
+
if representation_id and len(descriptive_ids) != 1:
|
|
326
|
+
reasons.append(f"linked representation has {len(descriptive_ids)} descriptive items")
|
|
327
|
+
if descriptive_item_id:
|
|
328
|
+
item = _entity_named(step.get(descriptive_item_id), "DESCRIPTIVE_REPRESENTATION_ITEM")
|
|
329
|
+
if item is None or len(item.params) < 2:
|
|
330
|
+
reasons.append(f"descriptive item {descriptive_item_id} is malformed")
|
|
331
|
+
else:
|
|
332
|
+
text = _text(item.params[1])
|
|
333
|
+
if not text:
|
|
334
|
+
reasons.append("manufacturing requirement has no authoritative text")
|
|
335
|
+
|
|
336
|
+
exact_callouts = tuple(
|
|
337
|
+
callout_id
|
|
338
|
+
for callout_id, name in callout_names.items()
|
|
339
|
+
if representation_name and name.casefold() == representation_name.casefold()
|
|
340
|
+
)
|
|
341
|
+
semantic_tokens = _name_tokens(semantic_name)
|
|
342
|
+
callout_ids = exact_callouts or tuple(
|
|
343
|
+
callout_id
|
|
344
|
+
for callout_id, name in callout_names.items()
|
|
345
|
+
if semantic_tokens and semantic_tokens <= _name_tokens(name)
|
|
346
|
+
)
|
|
347
|
+
missing_callout_associations = tuple(
|
|
348
|
+
callout_id for callout_id in callout_ids if not callout_aspects.get(callout_id)
|
|
349
|
+
)
|
|
350
|
+
if missing_callout_associations:
|
|
351
|
+
reasons.append(
|
|
352
|
+
"matched semantic callout(s) have no shape-aspect association: "
|
|
353
|
+
+ ", ".join(missing_callout_associations)
|
|
354
|
+
)
|
|
355
|
+
shape_aspect_ids = tuple(
|
|
356
|
+
dict.fromkeys(
|
|
357
|
+
aspect_id
|
|
358
|
+
for callout_id in callout_ids
|
|
359
|
+
for aspect_id in callout_aspects.get(callout_id, ())
|
|
360
|
+
)
|
|
361
|
+
)
|
|
362
|
+
missing_aspect_items = tuple(
|
|
363
|
+
aspect_id for aspect_id in shape_aspect_ids if not aspect_items.get(aspect_id)
|
|
364
|
+
)
|
|
365
|
+
if missing_aspect_items:
|
|
366
|
+
reasons.append(
|
|
367
|
+
"associated shape aspect(s) have no representation items: "
|
|
368
|
+
+ ", ".join(missing_aspect_items)
|
|
369
|
+
)
|
|
370
|
+
reference_item_ids = tuple(
|
|
371
|
+
dict.fromkeys(
|
|
372
|
+
item_id
|
|
373
|
+
for aspect_id in shape_aspect_ids
|
|
374
|
+
for item_id in aspect_items.get(aspect_id, ())
|
|
375
|
+
)
|
|
376
|
+
)
|
|
377
|
+
facts.append(
|
|
378
|
+
ManufacturingRequirementFact(
|
|
379
|
+
entity_id=entity_id,
|
|
380
|
+
semantic_name=semantic_name,
|
|
381
|
+
text=text,
|
|
382
|
+
representation_id=representation_id,
|
|
383
|
+
descriptive_item_id=descriptive_item_id,
|
|
384
|
+
callout_ids=tuple(dict.fromkeys(callout_ids)),
|
|
385
|
+
shape_aspect_ids=shape_aspect_ids,
|
|
386
|
+
reference_item_ids=reference_item_ids,
|
|
387
|
+
reason="; ".join(dict.fromkeys(reasons)),
|
|
388
|
+
)
|
|
389
|
+
)
|
|
390
|
+
return tuple(facts)
|
|
391
|
+
|
|
392
|
+
|
|
161
393
|
def _unit_factor_mm(step, unit_ref: str) -> tuple[float | None, str]:
|
|
162
394
|
unit = step.get(unit_ref)
|
|
163
395
|
if unit is None:
|
|
@@ -830,6 +830,37 @@ def leader_callout_geometry(tip, elbow, draft, *, text_side="auto", callout_box=
|
|
|
830
830
|
return label_box, segments
|
|
831
831
|
|
|
832
832
|
|
|
833
|
+
def analytical_leader_lands_clear(
|
|
834
|
+
candidate,
|
|
835
|
+
obstacles,
|
|
836
|
+
silhouette,
|
|
837
|
+
page,
|
|
838
|
+
*,
|
|
839
|
+
label,
|
|
840
|
+
geom_clear=False,
|
|
841
|
+
) -> bool:
|
|
842
|
+
"""Apply the producer's label/full-ink clearance floor to an unbuilt leader."""
|
|
843
|
+
label_box = candidate.label_box
|
|
844
|
+
if not label or label_box is None:
|
|
845
|
+
return False
|
|
846
|
+
check_box = label_box
|
|
847
|
+
if geom_clear:
|
|
848
|
+
xs = [label_box[0], label_box[2]]
|
|
849
|
+
ys = [label_box[1], label_box[3]]
|
|
850
|
+
for polygon in candidate.ink_polygons:
|
|
851
|
+
xs.extend(point[0] for point in polygon)
|
|
852
|
+
ys.extend(point[1] for point in polygon)
|
|
853
|
+
check_box = (min(xs), min(ys), max(xs), max(ys))
|
|
854
|
+
return not (
|
|
855
|
+
_box_hits(check_box, obstacles)
|
|
856
|
+
or _box_hits(label_box, [silhouette])
|
|
857
|
+
or label_box[0] < page[0]
|
|
858
|
+
or label_box[1] < page[1]
|
|
859
|
+
or label_box[2] > page[2]
|
|
860
|
+
or label_box[3] > page[3]
|
|
861
|
+
)
|
|
862
|
+
|
|
863
|
+
|
|
833
864
|
def leader_footprint(tip, elbow, draft, *, text_side="auto", callout_box=None):
|
|
834
865
|
"""Analytical page-mm AABB ``(x0, y0, x1, y1)`` of the :class:`Leader` that
|
|
835
866
|
``Leader(tip, elbow, label="", draft, text_side=…, callout=…)`` would build —
|
|
@@ -1761,14 +1792,24 @@ class PlacementContext:
|
|
|
1761
1792
|
"""Place an annotation onto the drawing through this context (#817) — the render passes'
|
|
1762
1793
|
door to the placement primitive, so a pass never reaches into the ``Drawing`` (ADR 0005
|
|
1763
1794
|
§2). Registers *obj* under *name* (owning *view* + source *feature*) and appends it to the
|
|
1764
|
-
render list.
|
|
1795
|
+
render list. Opaque compiler provenance is resolved here, at the placement boundary,
|
|
1796
|
+
so public ``annotations_of(IR feature)`` / ``drop(IR feature)`` keep their value-based
|
|
1797
|
+
contract for immediate passes as well as corridor-drained candidates. Returns *obj*."""
|
|
1765
1798
|
if self.items is None:
|
|
1766
1799
|
raise ValueError(
|
|
1767
1800
|
"PlacementContext.place() needs the drawing's render list — construct the context "
|
|
1768
1801
|
"with items=dwg.items (the orchestrator and Drawing verbs already do; a unit test "
|
|
1769
1802
|
"calling a render helper must pass it too)."
|
|
1770
1803
|
)
|
|
1771
|
-
return place_annotation(
|
|
1804
|
+
return place_annotation(
|
|
1805
|
+
self.registry,
|
|
1806
|
+
self.items,
|
|
1807
|
+
obj,
|
|
1808
|
+
name,
|
|
1809
|
+
view,
|
|
1810
|
+
resolve_feature(feature),
|
|
1811
|
+
measurement,
|
|
1812
|
+
)
|
|
1772
1813
|
|
|
1773
1814
|
def feature_of_hole_at(self, location):
|
|
1774
1815
|
"""The IR hole/pattern feature whose member sits at model-space *location*, or ``None``
|