Files
solidcraft-upstream/examples/regressions/loft_cut_beside_round_hole.json
T
CypherNaugh_0xandClaude Opus 5.5 3086c32a9a A round hole elsewhere on a body no longer breaks a loft cut beside it: a loft cut from a pocket floor (556.68 x 99 down 4 mm to 80 x 80), with a 35 mm hole 66 mm away that it never touches, failed with a kernel panic in TroughFall, while the same cut without the hole, or with the hole made afterwards, worked
The loft's top lies in the pocket floor. truck's boolean cannot take coincident faces, and the exact polygon fallback (planar_boolean) only takes bodies whose faces are all planar, so the hole's cylinder ruled out the only path that handles this cut. planar_boolean now first tries a sliced form: when an axis-aligned slab holds the tool and none of the body's curved faces, the body is split on the slab's planes (planes clear of the body's own faces), the planar middle piece takes the exact boolean, and the pieces outside are glued back on along the cut faces (the middle piece is healed first so its edges match theirs). Pieces in several parts are refused rather than glued into one shell. split_by_plane's half-space code is now ops::half_space, which this uses.

Tests: loft_cut_beside_a_round_hole replays examples/regressions/loft_cut_beside_round_hole.json and checks the exact volume (slab - pocket - prismatoid - hole; fails on main). With the hole made after the loft, the body is the same (volume, area, 19 faces, 42 edges).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 17:57:34 -04:00

25 lines
2.1 KiB
JSON

{
"description": "A loft cut from a pocket floor beside a round hole it never touches (the hole made first). Slab 900 x 480 x 160, a 557.68 x 100 pocket 120 deep, a 35 mm hole 25 deep 66 mm from the pocket, then a loft cut from a rectangle on the pocket floor (0.5 mm inside its walls) down 4 mm to an 80 x 80 square. The loft's top lies in the pocket floor, and the hole's cylinder kept the exact planar boolean from taking it: the cut failed with a kernel panic. Result: the same body as with the hole made after the loft.",
"commands": [
{"command": "sketch.create", "params": {"plane": "XY", "offset": 690, "name": "Outline"}},
{"command": "sketch.rectangle.two_point", "params": {"p0": [0, -480], "p1": [900, 0]}},
{"command": "sketch.finish", "params": {}},
{"command": "solid.extrude", "params": {"distance": 160, "name": "Slab"}},
{"command": "sketch.create", "params": {"plane": "XY", "offset": 850, "name": "BowlTop"}},
{"command": "sketch.rectangle.two_point", "params": {"p0": [171.16, -151.16], "p1": [728.84, -51.16]}},
{"command": "sketch.finish", "params": {}},
{"command": "solid.extrude", "params": {"distance": -120, "operation": "cut", "name": "BowlPocket"}},
{"command": "sketch.create", "params": {"plane": "XY", "offset": 850, "name": "TapHole"}},
{"command": "sketch.circle.center", "params": {"center": [795, -101.16], "radius": 17.5}},
{"command": "sketch.finish", "params": {}},
{"command": "solid.extrude", "params": {"distance": -25, "operation": "cut", "name": "TapCut"}},
{"command": "sketch.create", "params": {"plane": "XY", "offset": 730, "name": "TroughInset"}},
{"command": "sketch.rectangle.two_point", "params": {"p0": [171.66, -150.66], "p1": [728.34, -51.66]}},
{"command": "sketch.finish", "params": {}},
{"command": "sketch.create", "params": {"plane": "XY", "offset": 726, "name": "DrainSeat"}},
{"command": "sketch.rectangle.two_point", "params": {"p0": [410, -141.16], "p1": [490, -61.16]}},
{"command": "sketch.finish", "params": {}},
{"command": "solid.loft", "params": {"sections": [{"sketch": "TroughInset"}, {"sketch": "DrainSeat"}], "operation": "cut", "name": "TroughFall"}}
]
}