Skip to content

openGrid: make the stacking interface layer match the faces it sits between - #136

Open
TheFou wants to merge 1 commit into
AndyLevesque:mainfrom
TheFou:fix/opengrid-interface-layer-geometry
Open

openGrid: make the stacking interface layer match the faces it sits between#136
TheFou wants to merge 1 commit into
AndyLevesque:mainfrom
TheFou:fix/opengrid-interface-layer-geometry

Conversation

@TheFou

@TheFou TheFou commented Jul 31, 2026

Copy link
Copy Markdown

The stacking interface layer is derived from a single tile cross-section, which does not match both of the faces it is sandwiched between. That has two separate consequences, one for Full and one for Heavy. Lite is correct as-is and is left untouched.

Full — the interface overhangs into the screw counterbores

Stacked Full tiles are all zflip()ed, so an interface sits between a tile's original bottom face (the plate below) and its original top face (the plate above). The Full tile profile is symmetric, so those two sections are identical except where screws are counterbored — and only the bottom one was used.

Measured on a 2×2 board with Screw_Mounting = "Corners":

Face Area
Top face of the plate below 534.400 mm²
Interface layer, before 534.400 mm²
Bottom face of the plate above 507.088 mm²

The 27.3 mm² difference is exactly the four screw head counterbores (ø7.2 against ø4.1). The interface is now the intersection of both sections and measures 507.088 mm², matching the face it meets.

Heavy — every other interface used the wrong profile

The Heavy branch called interfaceLayer2D with boardType="Lite" and topSide alternating per layer. But a Heavy tile is symmetric, and both faces meeting an interface are an openGrid top face. Even-numbered interfaces were therefore already correct, while odd-numbered ones used the Lite cut-face section — 0.8 mm ribs at the waist of the profile instead of the 1.5 mm of the top face.

After the fix, both interfaces of a 3-tile stack measure 510.868 mm² instead of alternating. This also explains why a 2-tile Heavy stack is byte-for-byte unchanged: it only has interface #0, which was already right.

A Heavy half-tile uses an asymmetric profile (full_tile_profile branches on Full_or_Lite == "Heavy"), so intersecting both sections would wrongly shrink it there — hence the separate boardType == "Heavy" path rather than reusing the Full one.

Also in this change

  • Collapses the redundant linear_extrude(projection(linear_extrude(projection(...)))) in interfaceLayer into a single extrusion.
  • Factors the cross-section out into a fullTileSection() helper.
  • Lite is deliberately untouched. Its tiles alternate orientation, so the two faces bordering any given interface are always of the same kind, and the existing single-projection approach is already exact.

Repro

openscad -o before.stl -D 'Full_or_Lite="Heavy"' -D 'Stack_Count=3' openGrid/openGrid.scad

Interface #0 (z 13.9–14.3) and interface #1 (z 28.3–28.7) have different cross-sections on main; they are identical after this change.

Validation

Differential against main at default parameters — for each board type and stack count, rendered both revisions and compared:

Config Result
Lite, Stack_Count 2 and 3 STL byte-for-byte identical
Heavy, Stack_Count 2 STL byte-for-byte identical
Full, Stack_Count 2 and 3 only the interface bodies differ; every plate identical bit for bit
Heavy, Stack_Count 3 only interface #1 differs; interface #0 and all plates identical

Decomposed into connected components: same body count and same z ranges before and after (Full stack 3: 0–6.8, 6.9–7.3, 7.4–14.2, 14.3–14.7, 14.8–21.6).

Both rendering backends — the differential above was re-run with --backend=Manifold and --backend=CGAL; both agree. Cross-backend volumes match to ~5e-6 %, which is the same tessellation noise main already shows on the $fn=30 screw cylinders.

Configuration sweep — 29 configurations (board sizes 1×1 to 5×5 across the three types, all 5 screw modes, all 3 chamfer modes, adhesive base, both Fill Space modes, Interface_Separation 0, Ironing mode): all render NoError and manifold.

MakerWorld compatibility — the constructs used (intersection() of 2D projections, if/else under linear_extrude) already appear in this file, and the change renders identically on the old CGAL backend, so it does not depend on anything recent. For reference, the Parametric Model Maker backend is a development build of OpenSCAD with Manifold enabled — per a Bambu employee statement relayed in an unofficial but sourced community reference (commit b550957, 2024-11-25, then c8fbef0, 2025-09-20, with BOSL2 pinned at 99fcfc6). There is no official documentation page for this; flagging the source quality rather than presenting it as authoritative.

🤖 Generated with Claude Code

The stacking interface layer was derived from a single tile section, which did
not match both of the faces it is sandwiched between.

Full: stacked tiles are all zflipped, so the interface sits between a tile's
original bottom face (the plate below) and its original top face (the plate
above). Those two sections are identical except where screws are counterbored,
and only the bottom one was used, so the interface overhung into the screw head
recesses of the plate above - 534.400 mm2 of interface against a 507.088 mm2
mating face on a 2x2 board with corner screws. It is now the intersection of
both sections.

Heavy: the interface was built from openGridLite with topSide alternating per
layer, but a Heavy tile is symmetric and both faces meeting an interface are an
openGrid top face. Even-numbered interfaces were therefore already correct while
odd-numbered ones used the Lite cut-face profile - 0.8 mm ribs instead of 1.5 mm.
It now uses the top face section on both sides. Note a Heavy half-tile uses an
asymmetric profile, so intersecting both sections would wrongly shrink it there.

Also collapses the redundant linear_extrude(projection(linear_extrude(projection
(...)))) in interfaceLayer into a single extrusion, and factors the section out
into fullTileSection().

Lite is unchanged: its tiles alternate orientation, so the two faces bordering
any given interface are always of the same kind.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant