Three review findings, all root causes in `engineering/mech/dimension.py`.
1. The ISO 286 fit path was unreachable from the module's own pipeline.
`resolve_tolerance` resolved `intent.fit` correctly, but nothing produced a
DimIntent carrying one: no feature and no segment has a fit, and
`dimension_intents` constructed every intent without `fit=`. Only a
hand-built DimIntent - which is what the tests used - reached the code, so
the spec's `mech_iso286_on_dimension` and the `mech_assembly` benchmark's
"ISO 286 fits on the bearing seats" had no route.
`dimension_intents(..., fits={key: code})` now stamps the code onto the
intent its key names, and the part's own diameters became addressable one
at a time through the new `segment_key(i, bore=...)` -> "segment[1]",
"segment[1].bore"; a feature's stay addressable by id. A key that names
nothing is refused with the names that exist, a malformed code is refused
where it is attached rather than at draw time, a fit on a reference
dimension or on one that already carries a tol is refused, and a fit on a
hole whose diameter moved into the hole table (which has no tolerance
column) is refused by name instead of vanishing. Task 7's planned
`dimension_part(..., fits=None)` passes straight through - the argument is
additive and keyword-only, so every call written against the pinned
signature keeps working.
The fit also now reaches the *text*: `intent_text` expands the `<>`
placeholder and appends the code, so `text_rects` measures the string that
is actually drawn and a hole that already names itself ("⌀6") keeps its own
text and gains the code.
2. An explicit tolerance was silently discarded when `tol` omitted "mode".
`mode = tol.get("mode", "none")` and `build_dim_override(0.05, 0.05,
"none", None)` returns `({}, None)` without raising, so a ±0.05 resolved,
echoed its own numbers back in the payload, and drew a dimension with no
DIMTOL/DIMTP/DIMTM at all. `DimIntent.tol` now has a schema - {"mode",
"upper", "lower"} and nothing else - validated by `_explicit_tol`: an
unknown key, a missing "mode", a non-dict, and deviations under a mode that
writes none are each refused by name. The mutual-exclusion guard reads
`tol is not None` rather than truthiness, so `tol={}` is refused too.
3. The ISO 129-1 "no measurement twice" test was vacuous and the property was
false. The old test built the key `_dedupe` uniquifies as the last step of
`dimension_intents`, so it could not fail; meanwhile `style="chain"` emitted
20, 40, 15 *and* 75 as four plain DIMENSIONs. `redundant_linear` now finds
the span that shorter ones tile end to end on the same axis and level, and
`dimension_intents` marks it as an auxiliary (reference) dimension - the new
`REFERENCE_FORMAT` "(<>)", which renders "(75)". The plan's own test still
holds (the 75 is present); it is no longer a fourth plain dimension. The
error is one-sided on purpose: a tiling that is found is real, a tiling the
greedy walk misses leaves the dimension plain, which is the direction the
repository's critique rule wants. Baseline and ordinate parenthesise
nothing, and two horizontal chains at different heights are not each other's
pieces.
Tests: the vacuous gate is replaced by four that fail against the pre-fix code
- `redundant_linear` on the shaft's plain intents returns (3,) there and ()
here; the parametrised "a tolerance is either refused or reaches the dimension
variables" gate resolves the mode-less shape to an empty DIMVAR override
there; and `fits=` is a TypeError there. `_dedupe` is now exercised through a
duplicate fed in via the view's own dims, which the collector does not
generate itself. 46 tests in tests/test_mech_dimension.py (was 23).
Live AutoCAD: no finding was measured live and no new ActiveX call was
written - this module is pure Python with no backend, no async and no DXF. A
DIMTOL probe against the running seat was attempted twice and
`GetActiveObject("AutoCAD.Application")` blocked past the ~90 s contention
window both times, so the live engine was unavailable; no scratch document was
created (the probe never got the application object) and none was left open.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
196 lines
3.3 KiB
Python
196 lines
3.3 KiB
Python
"""The mechanical part model, view engine and standards tables.
|
|
|
|
Merge rule for the four track-B/G branches: the re-exports below are additive
|
|
and alphabetical — keep every side's names.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from .dimension import (
|
|
DIM_STYLES,
|
|
REFERENCE_FORMAT,
|
|
TEXT_WIDTH_FACTOR,
|
|
TOL_KEYS,
|
|
dimension_intents,
|
|
hole_table_rows,
|
|
intent_text,
|
|
is_reference,
|
|
layout_dimensions,
|
|
measured_value,
|
|
redundant_linear,
|
|
resolve_tolerance,
|
|
segment_key,
|
|
text_rects,
|
|
)
|
|
from .features import (
|
|
FEATURE_KINDS,
|
|
AxialBore,
|
|
BendLine,
|
|
CentreHole,
|
|
Chamfer,
|
|
CornerChamfer,
|
|
CornerFillet,
|
|
CrossHole,
|
|
Feature,
|
|
Fillet,
|
|
GearTeeth,
|
|
Hole,
|
|
HoleCircle,
|
|
HolePattern,
|
|
Keyway,
|
|
ORingGroove,
|
|
Pocket,
|
|
RetainingGroove,
|
|
Slot,
|
|
Thread,
|
|
Undercut,
|
|
feature_from_dict,
|
|
mirror_about_x,
|
|
)
|
|
from .part import (
|
|
PART_KINDS,
|
|
PrismaticPart,
|
|
RevolvedPart,
|
|
Segment,
|
|
build_part,
|
|
inner_radius_at,
|
|
outer_radius_at,
|
|
outline_bbox,
|
|
part_length,
|
|
part_max_diameter,
|
|
part_to_dict,
|
|
point_in_outline,
|
|
segment_bounds,
|
|
validate_part,
|
|
)
|
|
from .primitives import (
|
|
ROLE_LAYER,
|
|
ROLES,
|
|
Arc,
|
|
Circle,
|
|
DimIntent,
|
|
HatchArea,
|
|
Line,
|
|
Poly,
|
|
Prim,
|
|
Pt,
|
|
Role,
|
|
Text,
|
|
bbox,
|
|
layer_for,
|
|
points_of,
|
|
rotate,
|
|
scale,
|
|
translate,
|
|
)
|
|
from .views import (
|
|
CIRCLE_SEGMENTS,
|
|
PROJECTIONS,
|
|
SECTION_PLANE,
|
|
SECTION_STYLES,
|
|
VIEW_KINDS,
|
|
Band,
|
|
View,
|
|
build_view,
|
|
cut_loops,
|
|
detail_marker_prims,
|
|
detail_view,
|
|
layout_origin,
|
|
normalise_plane,
|
|
outline_prims,
|
|
radial_profile,
|
|
section_profile_prims,
|
|
section_view,
|
|
silhouette_prims,
|
|
)
|
|
|
|
__all__ = [
|
|
"Arc",
|
|
"AxialBore",
|
|
"Band",
|
|
"BendLine",
|
|
"CIRCLE_SEGMENTS",
|
|
"CentreHole",
|
|
"Chamfer",
|
|
"Circle",
|
|
"CornerChamfer",
|
|
"CornerFillet",
|
|
"CrossHole",
|
|
"DIM_STYLES",
|
|
"DimIntent",
|
|
"FEATURE_KINDS",
|
|
"Feature",
|
|
"Fillet",
|
|
"GearTeeth",
|
|
"HatchArea",
|
|
"Hole",
|
|
"HoleCircle",
|
|
"HolePattern",
|
|
"Keyway",
|
|
"Line",
|
|
"ORingGroove",
|
|
"PART_KINDS",
|
|
"PROJECTIONS",
|
|
"Pocket",
|
|
"Poly",
|
|
"Prim",
|
|
"PrismaticPart",
|
|
"Pt",
|
|
"REFERENCE_FORMAT",
|
|
"ROLES",
|
|
"ROLE_LAYER",
|
|
"RetainingGroove",
|
|
"RevolvedPart",
|
|
"Role",
|
|
"SECTION_PLANE",
|
|
"SECTION_STYLES",
|
|
"Segment",
|
|
"Slot",
|
|
"TEXT_WIDTH_FACTOR",
|
|
"TOL_KEYS",
|
|
"Text",
|
|
"Thread",
|
|
"Undercut",
|
|
"VIEW_KINDS",
|
|
"View",
|
|
"bbox",
|
|
"build_part",
|
|
"build_view",
|
|
"cut_loops",
|
|
"detail_marker_prims",
|
|
"detail_view",
|
|
"dimension_intents",
|
|
"feature_from_dict",
|
|
"hole_table_rows",
|
|
"inner_radius_at",
|
|
"intent_text",
|
|
"is_reference",
|
|
"layer_for",
|
|
"layout_dimensions",
|
|
"layout_origin",
|
|
"measured_value",
|
|
"mirror_about_x",
|
|
"normalise_plane",
|
|
"outer_radius_at",
|
|
"outline_bbox",
|
|
"outline_prims",
|
|
"part_length",
|
|
"part_max_diameter",
|
|
"part_to_dict",
|
|
"point_in_outline",
|
|
"points_of",
|
|
"radial_profile",
|
|
"redundant_linear",
|
|
"resolve_tolerance",
|
|
"rotate",
|
|
"scale",
|
|
"section_profile_prims",
|
|
"section_view",
|
|
"segment_bounds",
|
|
"segment_key",
|
|
"silhouette_prims",
|
|
"text_rects",
|
|
"translate",
|
|
"validate_part",
|
|
]
|