Falling grains cast no shadow (t37)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+2
-2
@@ -352,9 +352,9 @@ Drawing (src/render/): browser only. This file: the renderer state R, colour tab
|
||||
- functions: glowIndex, thermIdx, pipeCellColor, pipeBodyCell, thinPipeColor, beltLike, buildSky, initRender, resizeRender, viewCells, updateCamera, snapCamera, screenToWorld, worldToScreenPx, renderMonoFont
|
||||
- variables: R, GLOW_W, GLOW_R, GLOW_G, GLOW_B, THERM, PC, TREE_R, TREE_G, TREE_B, BG, SKY_R, SKY_G, SKY_B
|
||||
|
||||
### src/render/light.js (103 lines)
|
||||
### src/render/light.js (104 lines)
|
||||
Propagated coloured light from glowing and burning cells.
|
||||
- functions: builtForLight, onBeltForLight, computeLight
|
||||
- functions: builtForLight, looseForLight, computeLight
|
||||
|
||||
### src/render/world.js (332 lines)
|
||||
Scale colour without changing the smooth-look class.
|
||||
|
||||
@@ -4,6 +4,14 @@ What agents have done, newest first. Add an entry for every piece of work: the d
|
||||
note or request it was for, what changed, the files touched, the tests run, and anything left
|
||||
open or not verified. Keep each entry short; the git history has the details.
|
||||
|
||||
## 2026-10-25: t37 follow-up: falling grains cast no shadows (Claude Code, Codex)
|
||||
|
||||
- The owner saw a single falling grain darken a column of sky. `onBeltForLight` became
|
||||
`looseForLight` (`src/render/light.js`): a loose stack that ends in air or gas within 12 cells
|
||||
(falling) is see-through, as are belt and filter loads; stacks resting on any solid or a tube
|
||||
part still shade.
|
||||
- `test/t_light.js`; `run_all.py --browser` ALL PASSED.
|
||||
|
||||
## 2026-10-25: t37 follow-up: belt loads cast no shadows (Claude Code, Codex)
|
||||
|
||||
- The owner saw grains riding a belt overhead still shading the ground. `onBeltForLight(i)` in
|
||||
|
||||
+5
-4
@@ -5,11 +5,12 @@
|
||||
function builtForLight(i, x, y) {
|
||||
return (PH[mat[i]] === PH_SOLID && !(flags[i] & F_NAT)) || blockP[(y >> 2) * BW + (x >> 2)] || mach[i] || part[i];
|
||||
}
|
||||
function onBeltForLight(i) {
|
||||
function looseForLight(i) {
|
||||
for (let depth = 0; depth <= 12 && i < N; depth++, i += W) {
|
||||
const m = mat[i], ph = PH[m];
|
||||
if (m === BELTL || m === BELTR || part[i] === P_FILTER) return true;
|
||||
if (ph !== PH_POWDER && ph !== PH_LIQUID) return false;
|
||||
if (part[i]) return false;
|
||||
if (ph !== PH_POWDER && ph !== PH_LIQUID) return m === EMPTY || ph === PH_GAS;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -40,7 +41,7 @@ function computeLight() {
|
||||
let y = roofY[x];
|
||||
for (; y < roofEnd; y++) {
|
||||
const i = y * W + x, ph = PH[mat[i]];
|
||||
if ((ph === PH_SOLID || ph === PH_POWDER || ph === PH_LIQUID) && !builtForLight(i, x, y) && !((ph === PH_POWDER || ph === PH_LIQUID) && onBeltForLight(i))) break;
|
||||
if ((ph === PH_SOLID || ph === PH_POWDER || ph === PH_LIQUID) && !builtForLight(i, x, y) && !((ph === PH_POWDER || ph === PH_LIQUID) && looseForLight(i))) break;
|
||||
}
|
||||
lightRoof[lx] = y;
|
||||
}
|
||||
@@ -50,7 +51,7 @@ function computeLight() {
|
||||
const wx = (lx0 + lx) * res, k = ly * lw + lx;
|
||||
if (wx < 0 || wy < 0 || wx >= W || wy >= H) { la[k] = 0; lr[k] = lg[k] = lb[k] = 0; continue; }
|
||||
const i = wy * W + wx, m = mat[i], ph = PH[m];
|
||||
la[k] = builtForLight(i, wx, wy) || ((ph === PH_POWDER || ph === PH_LIQUID) && onBeltForLight(i)) ? aAir : ph === PH_EMPTY ? (bgm[i] ? aGas : aAir) : ph === PH_GAS ? aGas : ph === PH_LIQUID ? aLiq : m === GLASS || m === ICE ? aGas : aSol;
|
||||
la[k] = builtForLight(i, wx, wy) || ((ph === PH_POWDER || ph === PH_LIQUID) && looseForLight(i)) ? aAir : ph === PH_EMPTY ? (bgm[i] ? aGas : aAir) : ph === PH_GAS ? aGas : ph === PH_LIQUID ? aLiq : m === GLASS || m === ICE ? aGas : aSol;
|
||||
let r = 0, g = 0, b = 0;
|
||||
if (wy < lightRoof[lx]) { r = skyK; g = skyK * 0.98; b = skyK * 0.94; }
|
||||
else if (wy < surfY[wx] && ph !== PH_SOLID) { const v = skyK * 0.9; r = v; g = v * 0.98; b = v * 0.95; }
|
||||
|
||||
+15
-7
@@ -44,9 +44,9 @@ for (const mode of ['full', 'fast']) {
|
||||
}
|
||||
check('lighting leaves the simulation roof unchanged', run('simRoof.every((y, x) => roofY[x] === y) && roofY[80] === 80'));
|
||||
for (const material of ['STONE', 'SAND', 'WATER']) {
|
||||
run(`setCell(96 * W + 80, ${material}, AMB[96], F_NAT); updateRoof(); computeLight();`);
|
||||
run(`setCell(96 * W + 80, ${material}, AMB[96], F_NAT); setCell(97 * W + 80, STONE, AMB[97], F_NAT); updateRoof(); computeLight();`);
|
||||
check(`${material} above the view still blocks daylight`, run('lightAt(R.lr, 80, 144) < 0.95'));
|
||||
run('setCell(96 * W + 80, EMPTY, AMB[96], 0);');
|
||||
run('setCell(96 * W + 80, EMPTY, AMB[96], 0); setCell(97 * W + 80, EMPTY, AMB[97], 0);');
|
||||
}
|
||||
run('updateRoof(); computeLight();');
|
||||
check('removing an obstruction restores daylight', run('lightAt(R.lr, 80, 144) === 1'));
|
||||
@@ -70,14 +70,17 @@ run(`
|
||||
setCell(80 * W + x, STONE, AMB[80], F_NAT);
|
||||
for (let y = 77; y < 80; y++) setCell(y * W + x, SAND, AMB[y], 0);
|
||||
}
|
||||
for (let y = 40; y <= 76; y += 2) setCell(y * W + 152, SAND, AMB[y], 0);
|
||||
updateRoof();
|
||||
`);
|
||||
for (const mode of ['full', 'fast']) {
|
||||
run(`SET.lighting = '${mode}'; computeLight();`);
|
||||
for (const channel of ['lr', 'lg', 'lb']) {
|
||||
const [belt, open, stone] = run(`[lightAt(R.${channel}, 80, 84), lightAt(R.${channel}, 152, 84), lightAt(R.${channel}, 232, 84)]`);
|
||||
const [belt, open, stone] = run(`[lightAt(R.${channel}, 80, 84), lightAt(R.${channel}, 160, 84), lightAt(R.${channel}, 232, 84)]`);
|
||||
check(`${mode} ${channel}: a loaded belt leaves daylight below it`, Math.abs(belt - open) < 1e-6, `${belt} / ${open}`);
|
||||
check(`${mode} ${channel}: sand on natural stone still shades below`, stone < open * 0.95, `${stone} / ${open}`);
|
||||
const [streamGround, openGround] = run(`[lightAt(R.${channel}, 152, 160), lightAt(R.${channel}, 160, 160)]`);
|
||||
check(`${mode} ${channel}: a falling stream leaves ground as bright as open sky`, Math.abs(streamGround - openGround) < 1e-6, `${streamGround} / ${openGround}`);
|
||||
}
|
||||
run('setCell(76 * W + 80, WATER, AMB[76], 0); updateRoof(); computeLight();');
|
||||
const a = run('lightAt(R.latt, 80, 76)');
|
||||
@@ -89,19 +92,24 @@ for (const [name, support, depth, gap, expected] of [
|
||||
['right belt', 'mat[i] = BELTR', 12, '', true],
|
||||
['filter', 'part[i] = P_FILTER', 12, '', true],
|
||||
['deep heap', 'mat[i] = BELTR', 13, '', false],
|
||||
['air gap', 'mat[i] = BELTR', 3, 'EMPTY', false],
|
||||
['gas gap', 'mat[i] = BELTR', 3, 'STEAM', false],
|
||||
['air gap', 'mat[i] = BELTR', 3, 'EMPTY', true],
|
||||
['gas gap', 'mat[i] = BELTR', 3, 'STEAM', true],
|
||||
['12 falling grains', '', 12, '', true],
|
||||
['13 falling grains', '', 13, '', false],
|
||||
['natural ledge', 'mat[i] = STONE; flags[i] = F_NAT', 3, '', false],
|
||||
['wall floor', 'mat[i] = STONE', 3, '', false],
|
||||
['tube', 'part[i] = P_TR', 3, '', false],
|
||||
['loaded tube', 'mat[i] = SAND; part[i] = P_TR', 3, '', false],
|
||||
]) {
|
||||
run(`
|
||||
for (let y = 40; y <= 53; y++) { const i = y * W + 300; mat[i] = SAND; part[i] = 0; flags[i] = 0; }
|
||||
{ const i = (40 + ${depth}) * W + 300; mat[i] = EMPTY; mat[i + W] = BELTR; ${support}; }
|
||||
${gap ? `mat[41 * W + 300] = ${gap};` : ''}
|
||||
`);
|
||||
check(`${name}: belt load detection`, run('onBeltForLight(40 * W + 300)') === expected);
|
||||
check(`${name}: loose cell detection`, run('looseForLight(40 * W + 300)') === expected);
|
||||
}
|
||||
run('mat[40 * W + 300] = SAND; mat[41 * W + 300] = EMPTY;');
|
||||
check('a single grain in mid-air is see-through', run('looseForLight(40 * W + 300)'));
|
||||
run('mat[(H - 1) * W + 300] = SAND;');
|
||||
check('belt load detection stops at the world bottom', run('!onBeltForLight((H - 1) * W + 300)'));
|
||||
check('loose cell detection stops at the world bottom', run('!looseForLight((H - 1) * W + 300)'));
|
||||
done();
|
||||
|
||||
Reference in New Issue
Block a user