engineering/mech/features.py is the nineteen typed features of spec section 5,
each answering the same four questions - is my placement legal, what do I
contribute to this view, to a cut face, and what dimension do I want - plus the
five structural hooks the view engine needs: removal_box, radial_edit,
corner_edit, outline_edit and omission. Pure arithmetic; a test asserts emitted
primitive tuples, not a screenshot.
Nothing restates a table the repository already owns: Keyway takes its width
and depth from engineering.keyway (DIN 6885-1) and is refused by name outside
that table's bore range; GearTeeth takes its outline from engineering.gear's
involute generator and is refused when its tip diameter disagrees with the
segment it sits on.
standards/threads.py transcribes ISO 261 Table 1 and Table 2 - first-choice
M1.6-M64 plus second-choice M14/M18/M22/M27/M33/M39/M45/M52/M60 - and computes
ISO 724's basic minor and pitch diameters from ISO 68-1's own formula, pinned
against the published values (M20x2.5 -> 17.294 / 18.376). The remaining
second-choice diameters, every third-choice diameter and fine pitches below M8
are not transcribed and are refused by name, never interpolated. The drawing
rules are ISO 6410: minor at 0.8 x major, a thread-length limit line across the
major, a three-quarter circle in the end view, and an internal thread whose
section hatch runs to the major diameter.
standards/{undercuts,rings,centres,orings}.py ship DIN 509, DIN 471/472,
DIN 332-1 and ISO 3601-2 with their SOURCE, their row-key contract and an
**empty** row set. That is deliberate and is the track's table rule taken at its
word: a row that could not be verified against the standard is not shipped,
because a wrong relief depth or groove diameter is a wrong workshop drawing
while a narrow table is merely narrow. Every lookup is refused by name and says
what to transcribe and where; the features accept explicit dimensions so the
geometry is fully exercised, and DIN 332 form R is refused outright rather than
approximated by the straight 60 degree cone of forms A and B. Filling any of
these tables later is a data edit with no code change, and a shape test already
fails any row that lands without a column the drawing code reads.
Three deviations from the plan text, each measured rather than assumed
(the merge agent should keep exactly one copy of the first):
1. standards/__init__.py (Task 1, on main) refused a registration with no rows
("registered no rows"), which would have made the four not-transcribed
tables impossible to register at all - and registering them is what keeps a
later transcription a data edit. register() now refuses a rows argument that
is not a dict and accepts an empty one; two tests in tests/test_mech_standards.py
pin both halves. No existing test asserted the old refusal.
2. ORingGroove's housing kind is carried by `housing`, not `kind`. A dataclass
field named `kind` overwrites the discriminator _Base.to_dict() writes:
measured, Probe(id='og').to_dict() == {'kind': 'radial', 'id': 'og'}, so
feature_from_dict would then refuse its own payload as an unknown feature
kind and no part carrying an O-ring groove could round-trip through XDATA.
A new test pins the round trip.
3. The plan's end-chamfer test asserted after == (75.0, 10.0) - the corner
itself - and before == (72.0, 8.268), a point on neither surface. The real
edit is before (72.0, 10.0) on the cylinder and after (75.0, 8.268) on the
end face, consistent with the 45-degree start chamfer the same file pins.
The test was corrected to the measured geometry; the behaviour is unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
128 lines
2.0 KiB
Python
128 lines
2.0 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 .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,
|
|
)
|
|
|
|
__all__ = [
|
|
"FEATURE_KINDS",
|
|
"PART_KINDS",
|
|
"ROLES",
|
|
"ROLE_LAYER",
|
|
"Arc",
|
|
"AxialBore",
|
|
"BendLine",
|
|
"CentreHole",
|
|
"Chamfer",
|
|
"Circle",
|
|
"CornerChamfer",
|
|
"CornerFillet",
|
|
"CrossHole",
|
|
"DimIntent",
|
|
"Feature",
|
|
"Fillet",
|
|
"GearTeeth",
|
|
"HatchArea",
|
|
"Hole",
|
|
"HoleCircle",
|
|
"HolePattern",
|
|
"Keyway",
|
|
"Line",
|
|
"ORingGroove",
|
|
"Pocket",
|
|
"Poly",
|
|
"Prim",
|
|
"PrismaticPart",
|
|
"Pt",
|
|
"RetainingGroove",
|
|
"RevolvedPart",
|
|
"Role",
|
|
"Segment",
|
|
"Slot",
|
|
"Text",
|
|
"Thread",
|
|
"Undercut",
|
|
"bbox",
|
|
"build_part",
|
|
"feature_from_dict",
|
|
"inner_radius_at",
|
|
"layer_for",
|
|
"mirror_about_x",
|
|
"outer_radius_at",
|
|
"outline_bbox",
|
|
"part_length",
|
|
"part_max_diameter",
|
|
"part_to_dict",
|
|
"point_in_outline",
|
|
"points_of",
|
|
"rotate",
|
|
"scale",
|
|
"segment_bounds",
|
|
"translate",
|
|
"validate_part",
|
|
]
|