The first coherence pass still let wedges stand beside lower terraces or the void, exposing their bare triangular side profile (staircases of spikes on terraced rims, pyramid slivers on promontories). A slope wedge now additionally requires BOTH side flanks within a course of its own level; corner diagonals satisfy this by construction (their side dirs are the closed backs). Adjacent-open cells without a free corner keep the cube (their wedge always exposed a side toward the other opening).
- commit
- fcd127de7b1d89e7160e36fc0696d44650170f93
- parent
- 28d443810e581761da9fb38b07aa116e5afafdde
- author
- Dami <dami.etoile@gmail.com>
| M | src/ | 48 | ++++++++++++++++++------------ |
@@ -351,15 +351,36 @@ export function buildIsland(
351351 352352 // Waterfall column: waterfallTop at the crest, waterfall faces below.353353 const isFall = k === waterfallCell && open.length > 0;354- // Shoulder trim on plain edge tops (never on the waterfall). Support355- // law: a wedge leans against mass, so its high side must be CLOSED —356- // a cell open on two OPPOSITE sides (1-wide ridge) keeps the full357- // cube instead of a wedge with a floating high edge.354+ // Shoulder trim on plain edge tops (never on the waterfall). WFC355+ // wedge laws (USER RULE: triangles must never read jagged):356+ // 1. HIGH side closed — no wedge on opposite-open ridges (its high357+ // edge would float);358+ // 2. SIDE faces flanked at the wedge's own level (side neighbor top359+ // within a course, or higher) — a wedge beside a lower terrace or360+ // the void shows its bare triangular profile, and terraced rims361+ // become staircases of spikes.362+ // A corner diagonal satisfies law 2 by construction: its two side363+ // dirs are exactly the closed backs.358364 const adjacentOpen = open.length === 2 &&359365 open[0][0] !== open[1][0] && open[0][1] !== open[1][1];366+ const dirs = open.map(([dx, dz]) => dirOf(dx, dz));367+ const corner = adjacentOpen &&368+ dropAt(c, open[0][0] + open[1][0], open[0][1] + open[1][1]) >= 0.99;369+ const slopeDir = open.length === 0370+ ? -1371+ : open.length === 1372+ ? dirs[0]373+ : dropAt(c, open[1][0], open[1][1]) > dropAt(c, open[0][0], open[0][1])374+ ? dirs[1]375+ : dirs[0];376+ const flanked = slopeDir >= 0 &&377+ dropAt(c, DIRS[(slopeDir + 1) % 4][0], DIRS[(slopeDir + 1) % 4][1]) <378+ 0.99 &&379+ dropAt(c, DIRS[(slopeDir + 3) % 4][0], DIRS[(slopeDir + 3) % 4][1]) <380+ 0.99;360381 const trim = trimIsland && !isFall && !c.onRun && c.dist >= 1 &&361382 c.depth >= 1 &&362- (open.length === 1 || adjacentOpen);383+ (corner || (open.length === 1 && flanked));363384 364385 // One face per waterfall column — water can't zigzag around the365386 // corner course to course (the old per-course re-pick did).@@ -389,20 +410,9 @@ export function buildIsland(
389410 role: "support",390411 });391412 } else if (course === 0 && trim) {392- // Slope toward the single open side; diagonal across a corner.393- // USER RULE: the wedge must respect the surrounding surface — the394- // diagonal cut needs the corner cell BETWEEN its two open sides395- // open too (else the cut face jams into that block's corner), and396- // with two open sides the slope descends toward the LOWER one.397- const dirs = open.map(([dx, dz]) => dirOf(dx, dz));398- const corner = adjacentOpen &&399- dropAt(c, open[0][0] + open[1][0], open[0][1] + open[1][1]) >= 0.99;400- const slopeDir = open.length === 1401- ? dirs[0]402- : dropAt(c, open[1][0], open[1][1]) >403- dropAt(c, open[0][0], open[0][1])404- ? dirs[1]405- : dirs[0];413+ // Slope toward the single open side; diagonal across a corner414+ // (the cut needs the cell BETWEEN its two open sides open too,415+ // else the cut face jams into that block's corner).406416 placements.push({407417 pieceId: `${NATURE}:${408418 corner ? "cliff_blockDiagonal" : "cliff_blockSlope"