fix(task-9): put the fastener plan in projection with its elevation, and refuse a thread size that is not d
Two wrong-drawing bugs in SECTION 22.
1. _hex_points defaulted to start_deg=0, putting vertices at 3 and 9 o'clock,
so the plan of an M12 hex head measured 18 mm (across flats) across the
vertical while the elevation drew that same head 20.78 mm tall (across
corners e, silhouette edges at +/- e/4 - the projection of a hexagon with
vertices top and bottom). Place the two blocks in projection and the two
views of one part disagree. The default is now 30 deg, named HEX_START_DEG
and documented on both the constant and _hex_points as the convention the
elevation is the projection of, so a future fourth caller cannot pick the
other one by accident. The three top builders (_hex_fastener_top,
_hex_nut_top, _socket_head_top) inherit it.
2. _thread_feature read the pitch from params['size'] via the ISO 261 table
while drawing the geometry from params['d'], never cross-checking the two.
feature_spec('thread', {'d': 8.0, 'length': 20.0, 'size': 'M20'}) drew an M8
minor diameter and returned dims['pitch'] = 2.5 with ISO 261 cited as the
source - one thread's pitch on another thread's geometry, a silent wrong
number carrying a standards citation. A mismatch is now refused by name,
like every other check in the module. std_feature_draw's params description
says so.
Guard tests: the old top-view test asserted only len(points) == 6 and never a
vertex coordinate, so the orientation was untested. Added the vertex
coordinates, the plan-vs-elevation silhouette-height invariant across ISO 4014
/ 4017 / 4032, the socket screw's dk invariant, and the refused thread pair.
No ActiveX member is added or changed by either fix; both are pure geometry and
validation, so no live measurement is cited. The live AutoCAD could not be
probed for a confirmation run - GetActiveObject blocked past 150 s under
sibling contention.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
This commit is contained in:
co-authored by
Claude Opus 5
parent
84c1ef42fb
commit
bf8b3baf74
@@ -49,6 +49,7 @@ from engineering.mech.standards.fasteners import (
|
||||
across_corners,
|
||||
hex_head,
|
||||
hex_nut,
|
||||
nominal_diameter,
|
||||
sizes,
|
||||
socket_head,
|
||||
thread_length,
|
||||
@@ -70,6 +71,12 @@ DEFAULT_VIEW = "side"
|
||||
#: ISO 6410 draws the minor diameter of a thread at 0.8 x the major diameter.
|
||||
THREAD_MINOR_RATIO = 0.8
|
||||
|
||||
#: Hexagon orientation shared by the two views of one fastener: vertices at 12
|
||||
#: and 6 o'clock, so the plan is ``e`` tall across the vertical and the
|
||||
#: elevation - which draws the head ``e`` tall with its silhouette edges at
|
||||
#: ``+/- e / 4`` - is the projection of that same hexagon.
|
||||
HEX_START_DEG = 30.0
|
||||
|
||||
SOURCE_ISO_6410 = (
|
||||
"ISO 6410-1:1993 - technical drawings, screw threads and threaded parts, general conventions"
|
||||
)
|
||||
@@ -143,7 +150,16 @@ def _poly(points, closed: bool = True, layer: str = LAYER_VISIBLE) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def _hex_points(radius: float, start_deg: float = 0.0):
|
||||
def _hex_points(radius: float, start_deg: float = HEX_START_DEG):
|
||||
"""The six corners of a hexagon of across-corners radius ``radius``.
|
||||
|
||||
The default ``start_deg`` puts vertices at 12 and 6 o'clock, so the plan's
|
||||
vertical extent is ``2 * radius = e`` and the two remaining vertex pairs sit
|
||||
at ``+/- radius / 2``. That is exactly the silhouette the elevation builders
|
||||
draw (full height ``e``, chamfer lines at ``+/- e / 4``), which is what keeps
|
||||
the two views of one fastener in projection. Do not change it without
|
||||
changing the elevation to match.
|
||||
"""
|
||||
return [
|
||||
(
|
||||
radius * math.cos(math.radians(start_deg + 60.0 * i)),
|
||||
@@ -743,6 +759,15 @@ def _thread_feature(params: dict):
|
||||
internal = bool(params.get("internal", False))
|
||||
size = params.get("size")
|
||||
if size:
|
||||
nominal = nominal_diameter(size)
|
||||
if abs(nominal - d) > 1e-9:
|
||||
raise ValueError(
|
||||
f"thread: params['size']={size!r} is a {nominal} mm thread but "
|
||||
f"params['d']={d}. The geometry is drawn from d and the pitch is read "
|
||||
"from the ISO 261 table for size, so a mismatch would report one "
|
||||
"thread's pitch on another thread's geometry; give the size that "
|
||||
"matches d, or give params['pitch'] instead."
|
||||
)
|
||||
pitch = thread_pitch(size)
|
||||
elif "pitch" in params:
|
||||
pitch = _positive(params, "pitch", "thread")
|
||||
|
||||
Reference in New Issue
Block a user