Skip to content

Sheet metal: develop a surface of panels and joints into the flat (#6748) - #8397

Closed
h0witzer wants to merge 9 commits into
tangency-labs:mainfrom
h0witzer:fix-6748
Closed

h0witzer wants to merge 9 commits into
tangency-labs:mainfrom
h0witzer:fix-6748

Conversation

@h0witzer

@h0witzer h0witzer commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Change

Part of #6748: this is step 1, so merging it does not close the RFC.

Step 1 adds a development module that develops a surface of panels and joints into a flat region with exact curves:

  • Planes, cylinders and cones develop in closed form.
  • Free-form developable faces develop numerically, with stretch and shear reported.

It goes in new files only. Nothing users see changes.

Changes a default: no

Validation

To come:

  • Every existing sheet test's flat pattern is reproduced from the surface within 1e-6 mm.
  • The curved faces are checked against their analytic developments.

Integration status

The claim commit is on upstream main at 545232b.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
h0witzer and others added 5 commits October 28, 2026 20:58
…ncy-labs#6748)

Step 1 of the RFC. crates/develop holds the skin: panels drawn in charts
of their own, bends anchored at their virtual sharps (a hem at its
tangent lines), and rips. develop() lays the panels out through the
bends, returns each bend's zone and what each panel loses to its
setbacks, and reports a bend that closes a loop instead of forcing it.

crates/doc/src/sheet_skin.rs builds the skin of a sheet body from the
flat-first model and checks that developing it gives the sheet's flat.
With SOLIDCRAFT_SKIN_REPORT set to a file, every sheet body that is
built is checked: all 412 distinct bodies the engine's 221 sheet tests
build lay out to the same flat, with no area between the two (trays,
torn and slanted corners, hems, returns, folds, path flanges, flanges on
a flange's side edge, converted parts). A skin with a wrong K-factor is
caught. Nothing users see changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A panel can now lie on a cylinder or a cone: a roll, whose radius is
its geometry where a bend's is a value of the joint. Each panel goes
onto its neutral surface before it is laid out, so a roll flattens the
way a bend of its radius would. On a cylinder, lengths round it scale
by the neutral radius over the skin's: lines stay lines, and a circular
arc becomes exact elliptical conics. On a cone, distances from the apex
shift along the rulings: rulings and arcs about the apex stay exact,
anything else is followed by chords within 0.1 um.

A seam joins panels edge to edge with no zone, as where a roll meets a
flat panel. A rip cuts each side by its own amount and is not followed:
panels it keeps apart are laid out as pieces of their own, side by side,
and a ring of seams or bends is reported until a rip opens it. Every
flat segment says which segment of its panel it came from.

The sheet model's 412 bodies still lay out from their skins to today's
flat.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…abs#6748)

A bend's zone now names its near and far edges and its ends, and a trim
names the line along which it leaves the panel's material, so every edge
of a development is something in the skin: a panel's own segment, a
zone's edge or end, or a cut.

The flat is joined from those pieces by the planar booleans, which do
not yet say which input edge each output edge came from. Until they do,
sheet_flat_lineage.rs recovers it after the join, from the line or arc
each edge lies on, and counts the edges in tangency-labs#7990's classes. Over the
437 sheet states the engine's sheet tests build: 3722 exact, 1080
merged, 255 sources split into pieces, none found by geometry alone.
The round-trip tests now fail on any edge that resolves to nothing.

Every workaround for the planar booleans starts "Until the kernel's
planar booleans", so they can all be found and removed together.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…itted (tangency-labs#6748)

A panel now keeps its region in its own surface's parameters: a plane's
coordinates, a cylinder's length along and angle round its axis, a
cone's distance from its apex and angle round its axis. Flattening maps
those parameters to the flat, so the surface's flow (its parameter
lines) and anything drawn in its parameters carry to the flat exactly.
A cone's rulings and its circles round the axis are straight lines in
its parameters and flatten exactly, to lines and to arcs about the apex.

A free-form panel lies on a spline surface. Its flat map is a planar
spline over the surface's own breaks: linear along a straight direction,
where a development is exactly linear, and cubic along a curved one,
refined until the strain left is under 1e-7. It is fitted to the neutral
surface's metric, I - 2hII + h^2 III, so the thickness and K-factor are
in the fit rather than applied after it; a grid on the neutral surface
unfolded triangle by triangle is the starting guess. A straight line of
the parameters flattens to exact Bezier pieces, one per knot span; a
stretch the fit cannot avoid is reported with the flat, never forced.

A quarter cylinder written as a rational spline flattens to within
4e-9 mm of its neutral arc (strain 9e-8); a strip bent along a cubic to
within 5e-10 mm of its neutral curve's length (strain 1.7e-8); a saddle
reports 1.8% stretch. The sheet model's 437 states still lay out to the
same flat as today's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tangency-labs#6748)

Convert to Sheet Metal can now go through the skin: sheet.convert with
skin: true makes a new feature, sheet_skin_convert. No face is fixed.
Every face of a solid, or of one side of a shell or a plate, becomes a
panel of the sheet, less the faces the user skips. A cylinder or a cone
is a roll that keeps its modeled radius, joined by seams where the
sheet runs on smoothly. Every sharp edge between two faces of the sheet
is cut open, as today's Convert cuts it, until the user picks it as a
bend. A ring of bends is refused, naming its edges.

A solid's material lies inside its faces, or outside them with
direction: outside; a shell or a plate keeps its own thickness. The
body stays as modeled, and the model keeps the converted body's flat
pattern (saved in the model cache as skins), which sheet.flat_pattern
shows. The flat is drawn in the plane of the sheet's largest face.

The Convert dialog has a Through the skin switch: with it on, the inputs
are the body, the bend edges and the faces to skip, with the material's
side. Edit Feature brings them all back.

Tested against today's Convert: a channel with modeled corners converts
with no picks to the flat today's Convert makes from those corners as
bends, a shelled tray and a solid block with their floor edges picked
match it too, and a square tube opens where a corner is left unpicked.
The same block with its material outside is a tray T bigger all round,
and its flat is 4T longer each way. A block the size of the tray's
cavity, with its material outside, is the same metal as the tray; its
flat matches but at the corners, where its bend zones run the length of
the drawn edge rather than of the metal. That is fixed next.

Changes a default: no. The switch is off by default and today's Convert
is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
h0witzer and others added 3 commits October 29, 2026 09:18
…gency-labs#6748)

Converted through the skin, a bend's zone used to run the whole length
of its drawn edge. Where a rip cuts the panels at a corner, that edge is
the outside corner of the sheet when it is drawn from outside and the
inside corner when it is drawn from inside with its material out, so one
piece of metal unfolded two ways at its corners.

Now a bend is cut square at the rip's own plane, the one halfway between
the two ripped panels that already cuts their ends, so that none of its
metal comes nearer than half the gap to it. At a 90 degree corner that is
R + T + gap / sqrt 2 from the end of the edge drawn outside, and R +
gap / sqrt 2 from the end of the edge drawn inside: the same place. Past
a cut end the panels on either side stop at their tangent lines, the
corner's relief, whatever their tail: a bend's zone ends can now be
marked relieved, start and end apart. A base running on past a shorter
flange's free end still keeps its edge.

A tray shelled from a 100 x 60 x 30 box, the solid box itself, and a box
the size of the tray's cavity converted with its material outside now
unfold to the same flat, corners included, to 1e-6 mm2. The same box
with its material outside is a different tray, T bigger all round, and
its flat stays 4T longer each way.

The corners differ from today's Convert, whose bend zones run on into the
corner past the walls; the outlines' extents are unchanged.

Changes a default: no. Only Convert through the skin, which is new, is
affected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…corners and all (tangency-labs#6748)

Converted through the skin, a body used to stay the solid as modeled
while the model kept its flat pattern: a block stayed a block. Now the
body becomes the sheet metal part, sewn from the same pieces the flat
pattern is joined from, so the two can't disagree.

solidcraft-develop gains fold(), the other half of develop(). Each piece
of the flat goes where the caller says its first panel lies, and every
other panel where its bends and seams put it; a rip holds nothing. A
bend's metal is the ring sector between its inside and outside radii,
its flat zone wrapped onto it by the neutral arc. Any point of the flat,
at any depth through the sheet, then has its place in space: on a plane,
a roll (a cylinder or a cone, undone from its flat chart) or a bend.
Rigid3 joins Rigid2.

The solid is each piece's two faces, one on the skin and one a
thickness into the material, and a wall along each edge no other piece
shares. An edge folds to a line or an arc where it runs along or across
a bend or a roll, and to a chain of cubic pieces within 1e-7 mm
elsewhere. Convert places each piece by its first face, and checks that
every other face folds back onto itself: a joint read wrong is an error,
never a sheet in the wrong place.

No sheet comes first. Every panel is a sheet meeting the others at
bends and rips, and nothing about the result depends on which way a
joint runs or which panel is larger. Reliefs are values of the skin,
never drawn on a panel, so they move with the bends' radius and the
sheet's thickness:

- A bend's end can be notched (both panels stop at their tangent lines
  past it), slotted (the rule's bend relief, in whichever panel runs on
  past it), or torn tight against a plane: its metal kept off the plane
  by as much as at its two ends and, between, in proportion to its
  turn, so that a torn corner closes on the rip's plane all the way
  round the bend. The torn end is drawn in the flat as a cubic spline
  in the fewest even spans within a micron of that curve (a couple, on
  a tray), and folds to one smooth wall: the fold takes a run of
  free-form edges that join with no corner as one edge. A lap or a
  free-form rip will part the panels along other surfaces.
- A corner relief belongs to the corner, the two bends meeting there,
  not to any sheet round it: a round or a four-sided relief centered
  where the bends' zones' center lines cross, cut from every piece of
  the flat it reaches.

Convert reads the rule's corner relief, its size and its bend relief.
Two bends standing on one panel and meeting at a corner of it end where
their tangent lines on it cross, notched, with the corner's relief, or
torn tight against the plane of the rip between their other panels. A
bend that a rip cuts beside ends where its metal reaches the rip's
plane; a wall standing on part of another panel's edge bends only where
it stands. A relief that doesn't fit is left out with a note. This
replaces the gap/sqrt 2 the last commit cut off each bend at a corner:
two notched bends meet along a line and pucker into the open corner.
The sheet's side of a plate now takes a wall whose outside runs on in
one plane with the plate's edge, and never a face whose face behind is
the sheet's.

Checked, solid against solid: a shelled tray, a block drawn from outside
and the tray's cavity drawn from inside with its material out fold to
one solid and one flat for every corner relief; notched, that is today's
Convert of the shelled tray within 1e-9 of its volume, and the block
with its material outside today's Convert of a tray T bigger all round.
A tray whose walls outsize its floor gets the same four corner reliefs
as a shallow one. A torn edge, folded, keeps its metal off the rip's
plane by its share of the gap within a micron, through the whole
thickness. An icosahedron bent along a spanning tree of its faces bends
its triangles' whole edges, gets a round relief at each of the ten
corners where two bends meet on a face, and folds. A
plate with a wall on part of an edge folds to today's, bend relief
included. A square tube with three corners bent, and a channel whose
modeled rolls today's Convert bends, fold to today's solids within
2e-12.

Changes a default: no. Only Convert through the skin, which is new, is
affected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…at they are, no primary face (tangency-labs#6748)

WORK IN PROGRESS. This is a checkpoint, not a finished feature:
- The legacy infrastructure is not ripped out yet: the fixed-face Convert and the "skin" switch
  are still in place, and saved documents are not upgraded.
- Convert still reads the body's display tessellation for its faces' outlines, the edges between
  them, the sheet's thickness and the faces' areas. Rips along curved edges off a plane follow
  its facets. Every such place is marked "Until the kernel ... (tangency-labs#1597)".
- Landing the whole feature needs the kernel swap (tangency-labs#1597): exact B-rep topology and curves for
  every face, faces whole across a revolve, edges whole round any sweep. It also needs the
  topological naming stability work, so that sheet features can hold on to the flat's edges.

What is in:
- Rips are one operation. A rip carries its edge on each panel (as points, and exactly where the
  body knows it), the sheet's turn along it, and a lap. The cut at each point comes from the lap
  for the turn there, measured square to the edge with the panel's own surface metric, so a rip
  between two planes, round a roll's rim or between free-form panels is the same computation.
- Each panel's flat outline is built directly: every ripped stretch is replaced by its cut, and
  each cut is joined to the edge beside it where they cross, by carrying them on to where they
  meet (within a miter limit), or by a square step. Nothing is cut away by overlapping strips,
  so no edge is left in pieces, and every edge of the flat says what it is: the panel's own edge
  or a given rip's cut (EdgeFrom). The flat lineage reads that instead of guessing.
- Edges the kernel knows exactly in a plane (lines, arcs) are cut back exactly: a half round's
  rim stays one arc.
- A half-turn roll was laid a whole turn away from its seam (an angle read as 2 pi instead of 0):
  angles are now unwrapped from the middle of the panel's span. This was the mangled tombstone.
- There is no primary face: the walk starts on the largest face of any shape, a shell's
  thickness is measured between the exact surfaces, and each piece roots on its largest face.
  A cylinder or cone with no flat face converts. A ring of smooth seams (a tube) opens at the
  seam the walk did not cross; a ring of bends is still the user's to open.
- The folded solid splits an arc only past three quarters of a turn, as the kernel's profiles do.

Tests: a tombstone drawn from outside and from inside agrees and keeps its rims whole; a half
pipe and a whole pipe drawn as surfaces roll out to their exact widths; rip, join and exact-arc
tests in develop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@h0witzer

Copy link
Copy Markdown
Contributor Author

This work continues on develop, where the kernel is SMLib, in #9245. There, Convert reads the B-rep record instead of the display mesh. I'm keeping this PR open as the claim on main until you say which branch you want the sheet metal work on.

@h0witzer h0witzer changed the title Sheet metal: develop a skin of panels and joints into the flat (#6748) Sheet metal: develop a surface of panels and joints into the flat (#6748) Oct 30, 2026
@bherbruck

Copy link
Copy Markdown
Collaborator

Thanks for this! I'm closing older PRs that have gone quiet or drifted from main, since a lot has changed in the last couple of weeks. If you'd still like this in, please open a fresh PR against current main; happy to review it.

@bherbruck bherbruck closed this Nov 13, 2026
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