Skip to content

Catalog: answer a group with its whole reach curve, and keep the wall off the centreline - #6

Merged
JustinSGray merged 2 commits into
mainfrom
paul/tool_catalog
Sep 11, 2026
Merged

JustinSGray merged 2 commits into
mainfrom
paul/tool_catalog

Conversation

@JustinSGray

Copy link
Copy Markdown
Contributor

Two defects in the clearance drawing on the catalog's part page, found
together while looking at what a group is actually drawn against.

A group was answered against one of its features

The reach curve the drawing, the holder sweep and the tool list's
clears predicate were all judged against came off reading — the face
clicked last. A group of six was therefore answered against whichever of
the six that happened to be, and the verdict under the drawing changed
when nothing about the question had.

One tool for all of them has to get past all of their material, so the
curve is now the pointwise maximum of theirs. A reach curve is a
staircase of material heights by offset out from the cut, read up from
the bottom of the feature — which is where the tool tip sits when it is
in that feature — so every curve is already in the tool's own frame and
the maximum is exact rather than an approximation.

  • groupCurve (shared/group-geometry.ts) is the fold. Every input
    knot is a knot of the answer, since between two of them no input moves
    and so neither does the max; heights come from heightAt rather than
    a re-derived staircase, so the drawing and the sweep cannot disagree
    about where a rise comes; and a knot whose height the run after it
    repeats is dropped.
  • askedCurve beside it is the scope: asked()'s answer, not one
    feature of it, so the wall under the drawing and the column beside it
    are about the same thing. A group answered one tool each still reads
    the feature in front of it — every feature there gets its own tool, so
    there is nothing to fold.

The wall was drawn from the centreline

Every radius the overlay draws is cuttingRadius + offset, so the
wall's inner face is the cutting radius. (DC ?? 0) / 2 behind a
DC !== undefined guard put that face at r = 0 for any tool stating
no cutting diameter — the guard admits exactly the values ?? 0 then
swallows. geometry is typed Record<string, number>, but a catalog
built from a vendor that published no cutting diameter carries null
through the JSON, and null !== undefined. The hatch was drawn from the
centreline outward, straight through the tool it was meant to stand
clear of, covering its whole +r flank.

A positive diameter is now required. A tool that states none gets no
wall at all rather than one drawn from a radius nobody stated, and the
gaps go with it since there is no flank to measure from either.

Tests

  • group-geometry.test.ts — the fold sampled against the true pointwise
    max across a span of offsets, order-independence, knot trimming, the
    single-curve identity, a feature stating no curve, and the scope rule
    including the each exemption.
  • catalog-drawing.test.tsx — the wall stands off the cut by the tool's
    own radius; no diameter draws no wall; null and 0 draw no wall.
    Verified red against the previous guard.

pnpm check green. Catalog e2e: 122 passed, 2 skipped.

Not covered

The route wiring has no end-to-end test. Every click near the cube
fixture's centre resolves to the same feature, so a two-feature group
with differing curves cannot be built there honestly; the scope rule is
pinned as pure logic instead.

The curve the drawing, the sweep and the tool list were all judged
against came off `reading` — the face clicked last — so a group of six
was answered against whichever of the six that happened to be, and the
verdict changed when nothing about the question had.

One tool for all of them has to get past all of their material, so the
curve is now the pointwise maximum of theirs. A reach curve is a
staircase of heights by offset out from the cut, read up from the bottom
of the feature — which is where the tool tip sits when it is in that
feature — so every curve is already in the tool's own frame and the
max is exact rather than an approximation.

`groupCurve` in shared/group-geometry.ts is the fold: every input knot
is a knot of the answer, heights come from `heightAt` so the drawing and
the sweep cannot disagree about where a rise comes, and a knot whose
height the run after it repeats is dropped. `askedCurve` beside it is
the scope — `asked()`'s answer, not one feature of it. A group answered
one tool each still reads the feature in front of it, since every
feature there gets its own tool.
A reach curve is measured out from the cut, so every radius the overlay
draws is `cuttingRadius + offset` and the wall's inner face *is* the
cutting radius. `(DC ?? 0) / 2` behind a `DC !== undefined` guard put
that face at r = 0 for any tool stating no cutting diameter: the guard
admits exactly the values `?? 0` then swallows. `geometry` is typed
`Record<string, number>`, but a catalog built from a vendor that
published none carries `null` through the JSON, and null is not
undefined — so the hatch was drawn from the centreline outward, straight
through the tool it was meant to stand clear of.

A positive diameter is now required. A tool that states none gets no
wall at all rather than one drawn from a radius nobody stated, and the
gaps go with it since there is no flank to measure from either.
@JustinSGray
JustinSGray merged commit a85e30f into main Sep 11, 2026
1 check passed
pclauss123 added a commit that referenced this pull request Sep 11, 2026
Takes the 1.0.0 migration, the group reach curve and #6's material-wall
fix onto the clearance work. Three conflicts, all in the drawing seam:

- catalog-drawing.tsx: keeps this branch's extraction of the measurement
  into shared/assembly-gaps, and takes main's reservation semantics with
  it — MATERIAL_ROOM rather than the retired materialRoom prop. main's
  DEV ClearanceProbe is kept; its `outline !== null` guard goes, because
  gapsFor already returns null where the package draws no outline.

- tool-details.tsx: main's ratio rule for the sheet box, which its own
  e2e test pins — the sheet is as wide as the room or three quarters of
  its height, whichever is less. This branch's 22 rem floor under an
  18 rem cap was the flat cap that rule replaced.

- catalog-drawing.test.tsx: keeps main's new zoom sensor and this
  branch's comment on the test below it.

#6 fixed the cutting radius in catalog-drawing.tsx, which this branch
had already moved to shared/assembly-gaps, so the merge resolved it in
a file nothing reads any more and the defect would have survived it.
cuttingRadiusOf now returns `number | null` under main's guard, where
the three clearance boxes read it as well as the drawing — a fix applied
to the drawing alone would leave a box reading a gap taken from r = 0
while the picture beside it had stopped drawing one.
pclauss123 added a commit that referenced this pull request Sep 11, 2026
The guard #6 put on the drawing, as a sensor on the module that now owns
it. `!== undefined` was true of both shapes a silent vendor leaves — a
`DC` of `null` and a `DC` of `0` — so the wall was measured from `r = 0`
and drawn through the tool. Reverting cuttingRadiusOf to `(DC ?? 0) / 2`
fails both cases; nothing else in the suite notices.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants