Diagonal tubes as thick as straight ones, with centred elbows where they join (t35)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 22:40:52 -04:00
co-authored by Claude Opus 5.5
parent 814b51df46
commit 24eb9559f8
8 changed files with 171 additions and 44 deletions
+67 -18
View File
@@ -6,25 +6,66 @@ function fogColor(c, k) { return (c & 0xff000000) | (((c >>> 16 & 255) * k) << 1
const TUBE_CLASS = new Uint8Array(15); TUBE_CLASS[1] = 9; TUBE_CLASS[4] = 10; TUBE_CLASS[2] = 11; TUBE_CLASS[3] = 12;
for (let p = P_TUR; p <= P_TUL; p++) TUBE_CLASS[p] = 19 + p - P_TUR;
// Drawing only: the four transport lanes remain in the square block.
let diagonalJoins = new Uint8Array(0);
function diagonalTubeJoins(bx, by, v, planned = null) {
// Low/high three bits select the two edge exits; bits 6/7 mark free-end collars.
const dy = v === 4 || v === 6 ? -1 : 1;
let joins = 0;
for (let end = 0; end < 2; end++) {
const side = end ? 1 : -1, nx = bx + side, ny = by + side * dy;
const nb = ny * BW + nx, next = planned && planned.get(nb);
const nc = next ? next[0] : blockP[nb], nv = next ? next[1] : blockV[nb];
if (nx >= 0 && ny >= 0 && nx < BW && ny < BH &&
nc === BP_TUBE && nv >= 4 && (nv === 4 || nv === 6) === (dy < 0)) continue;
let join = 0;
for (const dir of [side > 0 ? 1 : 3, side * dy > 0 ? 2 : 0]) {
const x = bx + TUBE_DIRS[dir][0], y = by + TUBE_DIRS[dir][1];
if (x < 0 || y < 0 || x >= BW || y >= BH) continue;
const n = y * BW + x, next = planned && planned.get(n);
const code = next ? next[0] : blockP[n], nv = next ? next[1] : blockV[n];
if ((code === BP_TUBE && nv < 4 && (nv & 1) === (dir & 1)) ||
((code === BP_BELT || code === BP_FILTER) && (dir & 1))) { join = dir + 1; break; }
}
joins |= join << (end * 3);
if (!join) joins |= 64 << end;
}
return joins;
}
// Scratch coordinates shared by the pixel shading; no allocations in the grain loop.
let diagonalAcross = 0, diagonalAlong = 0, diagonalCap = 0;
function diagonalTubeShape(x, y, b, v, joins) {
const lx = x - (b % BW) * BS - 2, ly = y - ((b / BW) | 0) * BS - 2;
let best = Infinity;
for (let end = 0; end < 2; end++) {
const join = (joins >> (end * 3)) & 7, side = end ? 1 : -1;
const dx = join ? TUBE_DIRS[join - 1][0] : side * Math.SQRT1_2;
const dy = join ? TUBE_DIRS[join - 1][1] : side * (v === 4 || v === 6 ? -1 : 1) * Math.SQRT1_2;
const along = lx * dx + ly * dy, across = -lx * dy + ly * dx;
const radius = Math.hypot(across, Math.min(0, along)), cap = along - (join ? 2 : 2 * Math.SQRT2);
const sd = Math.max(radius - 2, join || (joins & (64 << end)) ? cap : -10);
if (sd < best) {
best = sd; diagonalAcross = side * across < 0 ? -radius : radius;
diagonalAlong = ((b % BW) * BS + 2 + (((b / BW) | 0) * BS + 2) * (v === 4 || v === 6 ? -1 : 1)) * Math.SQRT1_2 + side * along; diagonalCap = joins & (64 << end) ? cap : -10;
}
}
return best;
}
function diagonalTubeBlock(x, y, smooth) {
const i = y * W + x, own = (y >> 2) * BW + (x >> 2);
if (part[i] >= P_TUR && part[i] <= P_TUL) return own;
if (mat[i] !== EMPTY || part[i] || mach[i] || occ[i] || blockP[own]) return -1;
// Look in the four adjoining blocks for a pipe crossing this empty corner.
let best = smooth ? 0.75 : 0, found = -1;
for (let k = 0; k < 4; k++) {
const bx = (x >> 2) + (k === 0 ? -1 : k === 1 ? 1 : 0);
const by = (y >> 2) + (k === 2 ? -1 : k === 3 ? 1 : 0);
if (bx < 0 || by < 0 || bx >= BW || by >= BH) continue;
const b = by * BW + bx, v = blockV[b];
if (blockP[b] !== BP_TUBE || v < 4) continue;
const rising = v === 4 || v === 6, lx = x - bx * BS, ly = rising ? 3 - (y - by * BS) : y - by * BS;
if (Math.abs(lx - ly) > (smooth ? 3 : 1)) continue;
const side = lx + ly < 3 ? -1 : 1, nx = bx + side, ny = by + (rising ? -side : side);
if (nx < 0 || ny < 0 || nx >= BW || ny >= BH) continue;
const nb = ny * BW + nx;
if (blockP[nb] === BP_BELT || (blockP[nb] === BP_TUBE && (blockV[nb] < 4 || ((blockV[nb] === 4 || blockV[nb] === 6) === rising)))) return b;
const joins = diagonalJoins[b] === undefined ? diagonalTubeJoins(bx, by, v) : diagonalJoins[b];
const sd = diagonalTubeShape(x + 0.5, y + 0.5, b, v, joins);
if (sd <= best) { best = sd; found = b; }
}
return -1;
return found;
}
function drawWorld() {
const bw = R.bw, bh = R.bh, px = R.px;
@@ -44,6 +85,13 @@ function drawWorld() {
for (const i of moldCellsAt(mo.kind, mo.x, mo.y).walls) { const sx = (i % W) - vx0, sy = ((i / W) | 0) - vy0; if (sx >= 0 && sy >= 0 && sx < bw && sy < bh) mmask[sy * bw + sx] = 1; }
}
}
if (diagonalJoins.length !== NB) diagonalJoins = new Uint8Array(NB);
for (let by = Math.max(0, (vy0 >> 2) - 1); by <= Math.min(BH - 1, ((vy0 + bh) >> 2) + 1); by++) {
for (let bx = Math.max(0, (vx0 >> 2) - 1); bx <= Math.min(BW - 1, ((vx0 + bw) >> 2) + 1); bx++) {
const b = by * BW + bx;
if (blockP[b] === BP_TUBE && blockV[b] >= 4) diagonalJoins[b] = diagonalTubeJoins(bx, by, blockV[b]);
}
}
const useFog = SET.fog;
const OUT = sm ? 0x04000000 : 0xff000000; // unexplored and off the map: black
const lighting = SET.lighting !== 'off' && !R.lens;
@@ -178,22 +226,23 @@ function drawWorld() {
// field parts drawn over contents
let p = part[i];
const db = diagonalTubeBlock(x, y, sm);
let diagonalAcross = 0;
let diagonal = false;
if (db >= 0) {
const v = blockV[db], rising = v === 4 || v === 6;
diagonalAcross = x - (db % BW) * BS - (rising ? 3 - (y - ((db / BW) | 0) * BS) : y - ((db / BW) | 0) * BS);
p = sm || Math.abs(diagonalAcross) <= 1 ? TUBE_PARTS[v] : 0;
if (sm) tpx[row + bx] = 128 + diagonalAcross;
const v = blockV[db], joins = diagonalJoins[db];
const sd = diagonalTubeShape(x + 0.5, y + 0.5, db, v, joins);
p = sm || sd <= 0 ? TUBE_PARTS[v] : 0;
diagonal = !!p;
if (sm) tpx[row + bx] = (joins << 24) | ((y - ((db / BW) | 0) * BS + 8) << 16) | ((x - (db % BW) * BS + 8) << 8);
}
if (p && sm && (IS_TUBE[p] || p === P_FILTER)) { if (m === EMPTY || PH[m] === PH_GAS) { r = 0; g = 0; b = 0; } else { r = Math.max(1, r); } }
else if (p) {
if (IS_TUBE[p]) {
const on = netSat(i) > 0;
const vert = p === P_TU || p === P_TD, diagonal = TDX[p] && TDY[p];
const axis = diagonal ? (x * TDX[p] + y * TDY[p]) >> 1 : vert ? y : x;
const across = diagonal ? diagonalAcross + 1 : vert ? x & 3 : y & 3;
const dir = diagonal ? -1 : (p === P_TU || p === P_TL) ? 1 : -1;
if (diagonal ? across !== 1 : across === 0 || across === 3) { // copper casing on the two long sides; its load shows through
const vert = p === P_TU || p === P_TD;
const axis = diagonal ? Math.floor(diagonalAlong) : vert ? y : x;
const across = diagonal ? diagonalAcross + 2 : vert ? x & 3 : y & 3;
const dir = diagonal ? (blockV[db] >= 6 ? 1 : -1) : (p === P_TU || p === P_TL) ? 1 : -1;
if (diagonal ? Math.abs(diagonalAcross) > 1.38 || diagonalCap > -0.55 : across === 0 || across === 3) { // copper casing on the two long sides; its load shows through
if (m === EMPTY) { r = 150; g = 96; b = 64; } else { r = r * 0.72 + 44; g = g * 0.72 + 26; b = b * 0.72 + 14; }
if (!diagonal && (axis & 3) === 0) { r -= 24; g -= 18; b -= 12; }
} else {