83f192ac2ff4bed642a8d20fbe4ff68c43aab8a0
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
72bd17d024 |
feat(task-5): sections, cut faces, material hatch and detail views
engineering/mech/views.py grows the two remaining view kinds. A section's cut
faces come out of the same radial profile the silhouette does, so the hatched
area and the drawn outline are one calculation and cannot disagree: a solid
shaft's half-section loop measures exactly the sum of its segment rectangles,
and a bore closes the loop on itself rather than being hatched over.
ISO 6410's section convention falls out of the model rather than being special-
cased: an internal thread's radial_edit already raises the bore to the major
diameter, so the hatch stops there. ISO 128-3's rib rule is a flag on the
feature - a pocket marked no_section_hatch is drawn and left unhatched, while
the rest of the cut face is hatched with the part's ISO 128-50 material.
Four styles, each either implemented or refused by name with the alternative
rather than approximated: full and half on a longitudinal plane through a
revolved axis; full and revolved on a transverse plane (the annulus, flattened
at 2 degrees - 0.015% of the radius, stated in CIRCLE_SEGMENTS rather than
hidden); full and offset on a prismatic part, where through features split the
cut face and pockets reduce it to the remaining thickness. A plane that misses
the part, or that grazes an outline vertex so its crossings cannot be paired, is
refused instead of producing a guessed cut face.
Details scale a circular region of a parent view, emit the ISO 128-34 circle and
letter for the parent and the "A (5:1)" label for the detail itself, and report
every feature that falls outside the circle in omitted - the honesty rule again.
Three test assertions were corrected to the measured truth rather than the
engine bent to them, each verified by running the code:
* the ISO 6410 test asked for the whole cut-face loop's minimum to be the M20
major (r=10). It is not, and must not be: over the 5 mm that is drilled but
not threaded the loop closes on the real 16 mm drill (r=8). The loop is
((0,10),(0,25),(25,25),(30,25),(30,8),(30,8),(25,8),(25,10),(0,10)). The test
now pins both spans, which is the convention it meant to assert.
* the transverse-annulus areas were pinned at rel=2e-4. An inscribed regular
180-gon is exactly 1 - (n/2pi) sin(2pi/n) = 2.0307e-4 under the circle, so
2e-4 rejects the very flattening CIRCLE_SEGMENTS declares. The tolerance is
now 2.1e-4 and the test computes the deficit and pins it, so the number is
derived rather than guessed. CIRCLE_SEGMENTS' docstring now states both
figures: 0.015% radial (the 2 degree chord's sagitta) and 0.0203% of area.
* tests/test_mech_views.py pinned VIEW_KINDS == ("front","side","top"), which
this task deliberately grows. It now pins the three orthographic kinds as the
first three and the full five-kind tuple.
_is_longitudinal takes only the plane; the plan's signature also took the part
and never read it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
|
||
|
|
b2f858212f |
fix(task-4): the end-view occlusion rule, the double-drawn groove, the left view
Three defects in the orthographic view engine, all one missing idea: the engine
had no visibility rule and no single owner for the silhouette.
1. `_end_circles` inverted the hidden-line rule. Walking the bands from the
viewing end it drew a circle only when its radius EXCEEDED everything nearer,
and roled every such circle `hidden` unless it was the first band - so the one
edge that CANNOT be occluded (a step face larger than the material in front of
it) shipped dashed on HIDDEN, and the circle that really is covered was not
drawn at all. `_front_max` is now the rule: an edge of radius r at station x is
continuous when r is larger than every outer radius between x and the observer,
dashed when it is not, and it is always drawn. Bores follow the mirror image -
you see down a bore only while it stays at least as wide. Each distinct radius
is emitted once, because two coincident circles are a duplicate entity.
The same rule now roles a feature's axis-centred end-view circle, whose axial
station comes from `removal_box`; a feature that reports none keeps its own
verdict rather than having one guessed.
Ground truth by ray cast along the axis of the same solid of revolution
(segments 20x25, 40x30, 15x20; observer at +X, probe just inside each edge):
r=10 at x=75 -> first surface x=75 -> VISIBLE (was visible, kept)
r=15 at x=60 -> first surface x=60 -> VISIBLE (was HIDDEN, fixed)
r=12.5 at x=20 -> first surface x=60 -> HIDDEN (was not drawn, fixed)
2. Every axisymmetric groove was drawn twice - once as the notch
`radial_profile` cuts into the silhouette, once by the feature's own
`prims()`, which `_feature_layer` appended unconditionally. Six exactly
coincident LINEs per grooved shaft, which is precisely what
`critique.py::_check_duplicate_entities` matches, so premium rule 7 could
never be met on a part carrying a retaining or O-ring groove. `_profile_owned`
now drops a feature primitive that the body already emitted, and drops a
revolved front view's `visible` silhouette line from any feature that edits
the radial profile: the spec's rule is that grooves cut a notch in the
silhouette, and the notch belongs to the one calculation. Suppression is
"the profile already drew this", not "this feature edits the profile", so a
blind bore keeps the bottom line the profile cannot express, and an internal
thread keeps its ISO 6410 limit line.
A groove straddling a step was worse than duplicated: the feature reads one
surface radius for a span that has two, so it drew a groove floor through
solid material and left the +Y path with four odd-degree nodes. It now has
two - its two ends on the axis.
3. `build_view` validated `side` and put it in ctx, but the prismatic branch
ignored it: `side="left"` returned the right-hand view, with every feature on
the wrong side of the sheet and a symmetric outline rectangle to hide it. An
observer at -X sees screen-right = -Y, so the feature layer is now mirrored
about the outline's mid-Y. The top view is always seen from above and does
not move with `side`; that is now said in the docstring and pinned by a test.
Tests: the two that pinned the inverted rule are rewritten around the ray cast,
and one of them contradicted its own name. Seven tests added, and the prismatic
side-view test's hole moved off the y=25 mirror axis, where it could not tell a
left view from a right one. Nine of them fail against the previous `views.py`
and pass against this one.
Live AutoCAD was unavailable for a SOLPROF cross-check: the running AutoCAD 2026
(pid 40068) reports Responding=False and `GetActiveObject("AutoCAD.Application")`
never returned inside 100 s, so no scratch document was created and none is left
open. No new ActiveX member is called by this change - views.py is pure.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
|
||
|
|
48eb057273 |
feat(task-4): the orthographic view engine - silhouette, hidden lines, layout
engineering/mech/views.py projects a part model onto a view plane and returns primitive descriptions, so a test asserts geometry rather than a screenshot. The one calculation everything comes out of for a revolved part is the radial profile: the part cut into bands of constant bore and constant (or linearly tapering) outside, with every feature's radial_edit applied. The front view is that profile walked as a path and mirrored about the axis; the section cut face of the next task is the same profile walked as a closed loop, which is what stops the outline and the hatched area ever disagreeing. Chamfers and fillets do not draw over the silhouette - they substitute their corner in it, so the overshoot the critique focus looks for cannot be produced by construction, and a corner edit that finds no corner is reported rather than dropped. A band's radii are read off the segment the band's MIDPOINT lies in and the taper is evaluated at the band's own two ends. Sampling a hair inside the band to dodge the segment-boundary ambiguity - the way the plan's draft did - writes the taper slope times that inset into every coordinate the silhouette is built from: a 30 -> 10 mm cone over 20 mm then reports 14.99999 for a radius that is exactly 15, and nothing downstream can tell that it is wrong. The end view applies a real hidden-line rule: walking the bands from the viewing end, a diameter larger than everything nearer than it is drawn hidden, a bore behind material is hidden, and the near end's own circles are visible. Looking from the left reverses it. A revolved part's top view is refused, because it repeats the front. Prismatic parts get the outline in true shape with corner fillets and chamfers substituted in, and side/top thickness rectangles - the case where a hand-drawing model reliably forgets the hidden line per through feature, which here comes out of the feature itself. The honesty rule of spec section 6 is enforced in one place: a feature that emits no primitives and edits neither the radial profile nor a corner is listed in the view's omitted tuple with its own reason. Nothing is silently dropped. layout_origin places views in first-angle positions by default (the view from the right goes to the left, the view from above goes below) and flips both for third angle. It takes one additive keyword beyond the pinned signature, child, because placing a view to the left depends on that view's own width; without it the child is assumed the parent's size and every pinned call still works. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd |