update oak's pc
DramaticShape · 2026-08-01 ·
parent 6887f5d951
update oak's pc
Changed files
data/voxel_heights.lua | 49 +++++++++++++----
lib/Buildings.lua | 143 ++++++++++++++++++++++++++++++++++++++++++++++++
tools/building_voxels.py | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
3 files changed, 364 insertions(+), 27 deletions(-)
Diff
diff --git a/data/voxel_heights.lua b/data/voxel_heights.lua
index 7ee4004..cbf4fda 100644
--- a/data/voxel_heights.lua
+++ b/data/voxel_heights.lua
@@ -3270,16 +3270,21 @@ return {
roofRows = 19, roofBack = 16, roofFront = 0, roofCycle = { 2, 13 },
slab = 3, frontEave = 0, ledge = nil, depth = 2,
},
- -- F02: the computer bank on the lab's west table (OAKS_LAB cell
- -- 0,1) -- the one FACADE-ONLY template: rows 0-14 are machines
- -- drawn face-on (monitor and keys, the centre console, the right
- -- cabinet) over the lab-table fascia and base bands, and nothing
- -- is drawn from above. roofRows = 0, so the whole drawing
- -- extrudes -- the house facade treatment with no roof band; the
- -- roof fields are inert and the recess pass sinks every framed
- -- pane (screens, keys, the cabinet's inset face) into relief on
- -- its own. Same drawing, same grid, stands in the Hall of Fame
- -- on the GYM atlas -- registered there below.
+ -- F02: the computer desk on the lab's west side (OAKS_LAB cell
+ -- 0,1) -- the one DESK-SET template: the pipeline's region
+ -- classification at PART granularity (see Buildings
+ -- deskSetModel). The desk is the sibling lab table (fascia rows
+ -- 16-18, base 19-21); on it stand a monitor over its keyboard
+ -- (left), a computer tower over a keyboard and mouse (middle),
+ -- and a sheet of paper LYING FLAT (right). Upright parts anchor
+ -- their drawn bottom row to the desk's top plane and wear their
+ -- own drawn tops as lids; flat parts lie one voxel proud at
+ -- drawn row = depth row -- the same 1:1 the tabletop itself is
+ -- drawn with. The roof fields are inert (roofRows = 0 keeps the
+ -- recess scan over the whole drawing, which is what sinks the
+ -- monitor's screen and the tower's slots). Same drawing, same
+ -- grid, stands in the Hall of Fame on the GYM atlas --
+ -- registered there below.
{
id = "lab_computers",
tiles = {
@@ -3289,6 +3294,16 @@ return {
},
roofRows = 0, roofBack = 0, roofFront = 0, roofCycle = { 0, 0 },
slab = 0, frontEave = 0, ledge = nil, depth = 2,
+ desk = { fascia = { 16, 18 }, base = { 19, 21 } },
+ parts = {
+ { kind = "upright", x = { 2, 13 }, top = { 0, 2 },
+ facade = { 3, 10 }, depth = 4 }, -- the monitor
+ { kind = "flat", x = { 1, 13 }, rows = { 11, 14 } }, -- keyboard
+ { kind = "upright", x = { 14, 21 }, top = { 0, 3 },
+ facade = { 4, 10 }, depth = 6 }, -- the tower
+ { kind = "flat", x = { 14, 21 }, rows = { 11, 14 } }, -- keys+mouse
+ { kind = "flat", x = { 22, 30 }, rows = { 1, 14 } }, -- the paper
+ },
},
-- F03: the empty north table beside it (OAKS_LAB cell 2,1): the
-- starter table's band table verbatim on a grid two tiles
@@ -3307,8 +3322,8 @@ return {
GYM = {
-- F02 again: the Hall of Fame's recording machine is the lab's
- -- computer bank drawing, tile for tile, on the GYM atlas (one
- -- placement: HALL_OF_FAME cell 4,1). Same band table as the DOJO
+ -- computer desk drawing, tile for tile, on the GYM atlas (one
+ -- placement: HALL_OF_FAME cell 4,1). Same part table as the DOJO
-- entry above.
{
id = "lab_computers",
@@ -3319,6 +3334,16 @@ return {
},
roofRows = 0, roofBack = 0, roofFront = 0, roofCycle = { 0, 0 },
slab = 0, frontEave = 0, ledge = nil, depth = 2,
+ desk = { fascia = { 16, 18 }, base = { 19, 21 } },
+ parts = {
+ { kind = "upright", x = { 2, 13 }, top = { 0, 2 },
+ facade = { 3, 10 }, depth = 4 }, -- the monitor
+ { kind = "flat", x = { 1, 13 }, rows = { 11, 14 } }, -- keyboard
+ { kind = "upright", x = { 14, 21 }, top = { 0, 3 },
+ facade = { 4, 10 }, depth = 6 }, -- the tower
+ { kind = "flat", x = { 14, 21 }, rows = { 11, 14 } }, -- keys+mouse
+ { kind = "flat", x = { 22, 30 }, rows = { 1, 14 } }, -- the paper
+ },
},
},
},
diff --git a/lib/Buildings.lua b/lib/Buildings.lua
index 644cb09..d378ec7 100644
--- a/lib/Buildings.lua
+++ b/lib/Buildings.lua
@@ -307,11 +307,154 @@ end
-- ----------------------------------------------------------------- build --
+-- A desk with separately-classified objects on it (a template's `parts`
+-- list): the methodology's region classification at part granularity.
+-- Upright parts anchor their drawn bottom row to the desk's top plane
+-- and wear their own drawn tops as lids; flat parts (a keyboard, a
+-- sheet of paper) lie one voxel proud at drawn row = depth row -- the
+-- same 1:1 the tabletop itself is drawn with, so an object's height ON
+-- the drawing is its position ON the desk. The desk is the lab-table
+-- slab + base; its lid is the one synthesized surface in the model
+-- (the objects cover every drawn pixel of the tabletop), continued
+-- from the sibling tables' pattern in the drawing's own shades.
+-- tools/building_voxels.py `build_desk_set` is the reference twin.
+local function deskSetModel(sp, pr, t)
+ local W, H, D = sp.W, sp.H, pr.D
+ local ground = pr.ground
+ local col, inside = sp.col, sp.inside
+ local vox = {}
+ local function key(x, y, z) return (y * D + z) * W + x end
+ local function put(x, y, z, i) vox[key(x, y, z)] = i end
+
+ -- de-outline walk bounded to the part, so a part's side faces show
+ -- its own material and never the neighbour's (the sprite-wide walk
+ -- the facade path uses would cross the black seam between units)
+ local function interiorAt(sx, sy, lo, hi)
+ local i = sy * W + sx
+ if col[i] ~= BLACK then return sx end
+ local step = sx < math.floor((lo + hi) / 2) and 1 or -1
+ for d = 1, 3 do
+ local nx = sx + step * d
+ if nx >= lo and nx <= hi then
+ local ni = sy * W + nx
+ if inside[ni] and col[ni] ~= BLACK then return nx end
+ end
+ end
+ return sx
+ end
+
+ local f0, f1 = t.desk.fascia[1], t.desk.fascia[2]
+ local b0, b1 = t.desk.base[1], t.desk.base[2]
+ local plane = (b1 - b0 + 1) + (f1 - f0 + 1)
+
+ -- the base band, extruded exactly like every lab table's
+ for sy = b0, b1 do
+ Budget.tick()
+ local y = ground - 1 - sy
+ for sx = 0, W - 1 do
+ if inside[sy * W + sx] then
+ local ix = interiorAt(sx, sy, 0, W - 1)
+ for z = 0, D - 1 do
+ local px = (z == 0 or z == D - 1) and sx or ix
+ put(sx, y, z, sy * W + px)
+ end
+ end
+ end
+ end
+ for i in pairs(pr.recess) do
+ local sy = math.floor(i / W)
+ if sy >= b0 and sy <= b1 then
+ vox[key(i % W, ground - 1 - sy, D - 1)] = nil
+ end
+ end
+
+ -- the slab: fascia rows wrap every side; the lid continues the
+ -- sibling tables' top -- black rim, white highlight courses along
+ -- the north and west, grey field
+ for sy = f0, f1 do
+ Budget.tick()
+ local y = plane - 1 - (sy - f0)
+ for sx = 0, W - 1 do
+ for z = 0, D - 1 do put(sx, y, z, sy * W + sx) end
+ end
+ end
+ for sx = 0, W - 1 do
+ for z = 0, D - 1 do
+ local shade = GREY
+ if sx == 0 or sx == W - 1 or z == 0 or z == D - 1 then
+ shade = BLACK
+ elseif sx == 1 or z == 1 then
+ shade = WHITE
+ end
+ put(sx, plane - 1, z, pr.shadeTexel[shade])
+ end
+ end
+
+ local ytop = plane
+ for _, p in ipairs(t.parts) do
+ Budget.tick()
+ local x0, x1 = p.x[1], p.x[2]
+ if p.kind == "flat" then
+ for sy = p.rows[1], p.rows[2] do
+ if sy >= 0 and sy < D then
+ for sx = x0, x1 do
+ if inside[sy * W + sx] then put(sx, plane, sy, sy * W + sx) end
+ end
+ end
+ end
+ else
+ local tr0, tr1 = p.top[1], p.top[2]
+ local fr0, fr1 = p.facade[1], p.facade[2]
+ local pd = p.depth
+ local ytp = plane + (fr1 - fr0)
+ if ytp > ytop then ytop = ytp end
+ for sx = x0, x1 do
+ -- the lid: the part's drawn top laid across its depth from the
+ -- back, last row continuing forward; the front lid row is the
+ -- facade's own top row -- the drawn front-top edge
+ for z = 0, pd - 1 do
+ local front = z == pd - 1
+ local sy = front and fr0 or math.min(tr0 + z, tr1)
+ while sy <= tr1 and not inside[sy * W + sx] do sy = sy + 1 end
+ local ok = sy <= tr1 or (front and inside[fr0 * W + sx])
+ if ok then
+ put(sx, ytp, z, (front and fr0 or sy) * W + sx)
+ end
+ end
+ -- the body: facade rows anchored to the desk's top plane
+ for sy = fr0 + 1, fr1 do
+ local y = plane + (fr1 - sy)
+ local i = sy * W + sx
+ if inside[i] then
+ local ix = interiorAt(sx, sy, x0, x1)
+ for z = 0, pd - 1 do
+ if z == pd - 1 then
+ if not pr.recess[i] then put(sx, y, z, i) end
+ elseif z == 0 then
+ put(sx, y, z, i)
+ else
+ put(sx, y, z, sy * W + ix)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ return { at = function(x, y, z)
+ if x < 0 or x >= W or y < 0 or z < 0 or z >= D then return nil end
+ return vox[key(x, y, z)]
+ end,
+ W = W, ytop = ytop, zmin = 0, zmax = D - 1 }
+end
+
-- The voxel model as a lookup: `at(x, y, z)` is the index of the sprite
-- pixel that voxel wears, or nil. Build ORDER is expressed as lookup
-- order -- roof first, so it overwrites the walls it intersects, and walls
-- are trimmed to its underside so nothing pokes through the surface.
local function model(sp, pr, t)
+ if t.parts then return deskSetModel(sp, pr, t) end
local W, H, D = sp.W, sp.H, pr.D
local slab, roofRows = t.slab, t.roofRows
local top, ytop, ground = pr.top, pr.ytop, pr.ground
diff --git a/tools/building_voxels.py b/tools/building_voxels.py
index a38f8c5..6cb00b7 100644
--- a/tools/building_voxels.py
+++ b/tools/building_voxels.py
@@ -569,15 +569,21 @@ TEMPLATES = {
roof_rows=19, roof_back=16, roof_front=0, roof_cycle=(2, 13),
slab=3, front_eave=0, ledge=None, tileset="gym", depth=2,
),
- # F02: the computer bank on Oak's lab's west table -- and the Hall
- # of Fame's recording machine, the same drawing on the GYM atlas
- # (one placement each; both registered in voxel_heights). The one
- # FACADE-ONLY template: rows 0-14 are machines drawn face-on over
- # the lab-table fascia and base bands, nothing is drawn from above,
- # so roof_rows=0 and the whole drawing extrudes -- the house facade
- # treatment with no roof band. The recess pass turns every framed
- # pane (the monitor, its keys, the right cabinet's inset face) into
- # relief on its own.
+ # F02: the computer desk in Oak's lab -- and the Hall of Fame's
+ # recording machine, the same drawing on the GYM atlas (one
+ # placement each; both registered in voxel_heights). The one
+ # DESK-SET template: the methodology's region classification at
+ # part granularity. The desk is the sibling lab table (fascia rows
+ # 16-18, base 19-21); on it stand a monitor over its keyboard
+ # (left), a computer tower over a keyboard and mouse (middle), and
+ # a sheet of paper LYING FLAT (right). Upright parts anchor their
+ # drawn bottom row to the desk's top plane and wear their own drawn
+ # tops as lids; flat parts lie one voxel proud with drawn row =
+ # depth row -- the same 1:1 the tabletop itself is drawn with, so
+ # an object's height ON the drawing is its position ON the desk.
+ # The desk's own top is the one synthesized surface (the objects
+ # cover every pixel of it), continued from the sibling tables'
+ # pattern in the drawing's own shades.
"lab_computers": dict(
tiles=[
[91, 92, 93, 94],
@@ -586,6 +592,16 @@ TEMPLATES = {
],
roof_rows=0, roof_back=0, roof_front=0, roof_cycle=(0, 0),
slab=0, front_eave=0, ledge=None, tileset="gym", depth=2,
+ desk=dict(fascia=(16, 18), base=(19, 21)),
+ parts=[
+ dict(kind="upright", x=(2, 13), top=(0, 2), facade=(3, 10),
+ depth=4), # the monitor
+ dict(kind="flat", x=(1, 13), rows=(11, 14)), # its keyboard
+ dict(kind="upright", x=(14, 21), top=(0, 3), facade=(4, 10),
+ depth=6), # the tower
+ dict(kind="flat", x=(14, 21), rows=(11, 14)), # keys + mouse
+ dict(kind="flat", x=(22, 30), rows=(1, 14)), # the paper
+ ],
),
# F03: the empty north table beside it -- the starter table's band
# table verbatim on a grid two tiles narrower (one placement).
@@ -741,10 +757,122 @@ def profile(sp, t):
# --------------------------------------------------------------- stage 3 --
+def build_desk_set(sp, pr, t):
+ """A desk with separately-classified objects on it (the `parts` list):
+ upright parts stand on the desk's top plane wearing their own drawn
+ tops as lids, flat parts lie one voxel proud at drawn row = depth row,
+ and the desk itself is the lab-table slab + base with a synthesized
+ lid (the objects cover every drawn pixel of the tabletop)."""
+ W, H, D = pr["W"], pr["H"], pr["D"]
+ inside = pr["inside"]
+ col, src = sp["col"], sp["src"]
+ ground = pr["ground"]
+ vox = {}
+
+ def put(x, y, z, sx, sy):
+ vox[(x, y, z)] = (col[sy][sx], src[sy][sx])
+
+ shade_px = {}
+ for sy in range(H):
+ for sx in range(W):
+ if inside(sx, sy):
+ shade_px.setdefault(col[sy][sx], (sx, sy))
+
+ def interior(sx, sy, lo, hi):
+ if col[sy][sx] != BLACK:
+ return sx
+ step = 1 if sx < (lo + hi) // 2 else -1
+ for d in range(1, 4):
+ nx = sx + step * d
+ if lo <= nx <= hi and inside(nx, sy) and col[sy][nx] != BLACK:
+ return nx
+ return sx
+
+ f0, f1 = t["desk"]["fascia"]
+ b0, b1 = t["desk"]["base"]
+ plane = (b1 - b0 + 1) + (f1 - f0 + 1) # the desk's top plane
+
+ # the base band, extruded exactly like every lab table's
+ for sy in range(b0, b1 + 1):
+ y = ground - 1 - sy
+ for sx in range(W):
+ if not inside(sx, sy):
+ continue
+ ix = interior(sx, sy, 0, W - 1)
+ for z in range(D):
+ put(sx, y, z, sx if z in (0, D - 1) else ix, sy)
+ for sx, sy in pr["recess"]:
+ if b0 <= sy <= b1:
+ vox.pop((sx, ground - 1 - sy, D - 1), None)
+
+ # the slab: the fascia rows wrap every side; the lid is the one
+ # synthesized surface in the model -- the drawing never paints the
+ # tabletop (its objects cover it), so the lid continues the sibling
+ # tables' pattern in the drawing's own shades: black rim, white
+ # highlight courses along the north and west, grey field
+ for i, sy in enumerate(range(f0, f1 + 1)):
+ y = plane - 1 - i
+ for sx in range(W):
+ for z in range(D):
+ put(sx, y, z, sx, sy)
+ for sx in range(W):
+ for z in range(D):
+ if sx in (0, W - 1) or z in (0, D - 1):
+ shade = BLACK
+ elif sx == 1 or z == 1:
+ shade = WHITE
+ else:
+ shade = GREY
+ px = shade_px.get(shade) or shade_px[BLACK]
+ put(sx, plane - 1, z, px[0], px[1])
+
+ for p in t["parts"]:
+ x0, x1 = p["x"]
+ if p["kind"] == "flat":
+ r0, r1 = p["rows"]
+ for sy in range(r0, r1 + 1):
+ if not 0 <= sy < D:
+ continue
+ for sx in range(x0, x1 + 1):
+ if inside(sx, sy):
+ put(sx, plane, sy, sx, sy)
+ continue
+ tr0, tr1 = p["top"]
+ fr0, fr1 = p["facade"]
+ pd = p["depth"]
+ ytp = plane + (fr1 - fr0)
+ for sx in range(x0, x1 + 1):
+ # the lid: the part's drawn top laid across its depth from
+ # the back, last row continuing forward; the front lid row
+ # is the facade's own top row -- the drawn front-top edge
+ for z in range(pd):
+ sy = fr0 if z == pd - 1 else min(tr0 + z, tr1)
+ while sy <= tr1 and not inside(sx, sy):
+ sy += 1
+ if sy > tr1 and not (z == pd - 1 and inside(sx, fr0)):
+ continue
+ put(sx, ytp, z, sx, fr0 if z == pd - 1 else sy)
+ # the body: facade rows anchored to the desk's top plane
+ for sy in range(fr0 + 1, fr1 + 1):
+ y = plane + (fr1 - sy)
+ if not inside(sx, sy):
+ continue
+ ix = interior(sx, sy, x0, x1)
+ for z in range(pd):
+ if z == pd - 1:
+ if (sx, sy) not in pr["recess"]:
+ put(sx, y, z, sx, sy)
+ else:
+ put(sx, y, z, sx if z == 0 else ix, sy)
+ return vox
+
+
def build(sp, pr, t):
"""The voxel model. Order is load-bearing: walls, ledge, recesses, then
the roof solid overwrites what it intersects and the walls are trimmed
to the roof's underside."""
+ if t.get("parts"):
+ return build_desk_set(sp, pr, t)
W, H, D = pr["W"], pr["H"], pr["D"]
inside, top, ytop = pr["inside"], pr["top"], pr["ytop"]
col, src = sp["col"], sp["src"]
@@ -869,12 +997,13 @@ def verify(vox, sp, pr, t):
for (x, y, z), _ in vox.items():
assert y <= T(x), f"voxel pokes through the roof at {x},{y},{z}"
- # A facade-only template (roof_rows == 0: the lab computer bank) has
- # no roof band, so there is no surface to hold level or slope and no
- # coverage to demand of it; the poke-through assert above and the
- # facade asserts below are what it answers to. Everything with a roof
- # band answers the full set.
- if t["roof_rows"] == 0:
+ # A desk set answers its own asserts; a facade-only template (roof_rows
+ # == 0) has no roof band, so there is no surface to hold level or slope
+ # and no coverage to demand of it; everything with a roof band answers
+ # the full set.
+ if t.get("parts"):
+ verify_desk_set(vox, pr, t)
+ elif t["roof_rows"] == 0:
# its one geometric intent: a straight extrusion, so each column
# of the drawing is solid from its lowest voxel to its top. NOT
# from the ground -- the drawing's base band is inset like every
@@ -899,6 +1028,46 @@ def verify(vox, sp, pr, t):
return shell
+def verify_desk_set(vox, pr, t):
+ W, D = pr["W"], pr["D"]
+ f0, f1 = t["desk"]["fascia"]
+ b0, b1 = t["desk"]["base"]
+ plane = (b1 - b0 + 1) + (f1 - f0 + 1)
+
+ # the slab is a full solid under everything on it
+ for x in range(W):
+ for z in range(D):
+ for y in range(b1 - b0 + 1, plane):
+ assert (x, y, z) in vox, f"slab hole at {x},{y},{z}"
+
+ # each part stands where authored -- upright bodies solid from the
+ # desk to their own drawn top (bar the front layer, whose panes
+ # sink) -- and nothing stands anywhere else
+ tops = {}
+ for p in t["parts"]:
+ x0, x1 = p["x"]
+ if p["kind"] == "flat":
+ r0, r1 = p["rows"]
+ for x in range(x0, x1 + 1):
+ for z in range(max(r0, 0), min(r1, D - 1) + 1):
+ tops[(x, z)] = max(tops.get((x, z), 0), plane)
+ else:
+ fr0, fr1 = p["facade"]
+ ytp = plane + (fr1 - fr0)
+ for x in range(x0, x1 + 1):
+ for z in range(p["depth"]):
+ tops[(x, z)] = max(tops.get((x, z), 0), ytp)
+ if z == p["depth"] - 1:
+ continue
+ ys = [y for y in range(plane, ytp + 1)
+ if (x, y, z) in vox]
+ assert ys == list(range(ys[0], ys[0] + len(ys))) \
+ if ys else True, f"part column hole at {x},{z}"
+ for (x, y, z) in vox:
+ assert y <= tops.get((x, z), plane - 1), \
+ f"voxel above its part at {x},{y},{z}"
+
+
def verify_roof(vox, pr, t):
W, H, D = pr["W"], pr["H"], pr["D"]
ytop, top, slab = pr["ytop"], pr["top"], t["slab"]