engineering/mech/part.py is the model the whole track draws from: a revolved stack of segments along +X, or a closed prismatic outline plus a thickness, each carrying a list of typed features. Pure arithmetic - no backend, no async, no DXF - which is what lets the view engine be tested without a drawing. Every refusal fires before anything is drawn and names the offending path the way pid_from_spec does: a non-finite or non-positive number as segments[2].d_outer, a bore that is not smaller than its outside (a bore that changes diameter is two segments), an outline with fewer than three distinct vertices, an outline that crosses itself (engineering.measure.is_self_intersecting - the shoelace cancels crossed lobes rather than failing, so a bowtie would otherwise measure 0.0), an unknown material, an unknown segment key, a duplicate feature id. Two gates are the reason this is a model rather than a dict: a feature whose placement is off the part is refused by its own name and by the range it missed, and two features that would remove the same material are refused with both names and the overlapping box. Neither is clamped. part_to_dict round-trips through build_part, which is what lets mech_part_draw write the model onto the drawing as ACADMCP_MECH XDATA and mech_view_add read it back months later without the caller re-describing the part. Two fixes to the plan's own code and test list, both recorded in deviations: _features_from imported engineering.mech.features unconditionally, so a featureless part - every fixture in this module, and both round-trips - died on ModuleNotFoundError until Task 3 lands; the import now happens only when there is a feature dict to build. And the plan's test file omitted test_a_duplicate_feature_id_is_refused while its own gate deselects it by name, so the duplicate-id refusal the commit message claims went unpinned; the test is written here and Task 3 turns it green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
80 lines
1.3 KiB
Python
80 lines
1.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 .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__ = [
|
|
"PART_KINDS",
|
|
"ROLES",
|
|
"ROLE_LAYER",
|
|
"Arc",
|
|
"Circle",
|
|
"DimIntent",
|
|
"HatchArea",
|
|
"Line",
|
|
"Poly",
|
|
"Prim",
|
|
"PrismaticPart",
|
|
"Pt",
|
|
"RevolvedPart",
|
|
"Role",
|
|
"Segment",
|
|
"Text",
|
|
"bbox",
|
|
"build_part",
|
|
"inner_radius_at",
|
|
"layer_for",
|
|
"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",
|
|
]
|