import { useMemo } from 'react';
import {
CeilingBeams,
CorniceRing,
HangingFixture,
SideRails,
WallBand,
} from './primitives';
import {
shade,
useBayZ,
useCorners,
useRepeat,
type PeriodProps,
} from './geometry';
/**
* Fauvist studio — Fauvism.
*
* The Collioure summer room: close-set whitewashed rafters, plank shutters
* folded back against the wall beside every window, and a plain painted rail.
* The architecture stays vernacular so the wall colour does the work.
*/
export function FauvistDetails({ room, trim, windows }: PeriodProps) {
const rafters = useBayZ(room.depth, room.halfD, 1.5, 14);
const timber = '#d8c8a8';
const shutters = useMemo(
() =>
(windows ?? [])
.filter((w) => w.wall === 'left' || w.wall === 'right')
.flatMap((w) =>
[-1, 1].map((dir) => ({
wall: w.wall as 'left' | 'right',
z: w.x + dir * (w.width / 2 + w.width * 0.28),
y: w.y,
h: w.height,
w: w.width * 0.5,
}))
),
[windows]
);
return (
{/* Folded-back plank shutters flanking the windows */}
{shutters.map((s, i) => (
{[-0.3, 0, 0.3].map((t) => (
))}
))}
{(['left', 'right', 'back'] as const).map((wall) => (
))}
);
}
/**
* Expressionist room — Expressionism.
*
* Die Brücke's carved-plank interior pushed towards the raked sets of the
* period's cinema: battens canted at alternating angles above the hang, a
* zig-zag frieze, and ceiling members that refuse to run square.
*/
export function ExpressionistDetails({ room, trim }: PeriodProps) {
const bays = useBayZ(room.depth, room.halfD, 2.2, 11);
const plank = '#5a3f27';
const light = shade(plank, 0.3);
return (
{/* Canted battens, alternating lean */}
{bays.map((z, i) => (
{[-1, 1].map((side) => (
))}
))}
{/* Zig-zag frieze under the ceiling */}
{(['left', 'right'] as const).map((wall) =>
bays.map((z, i) => (
))
)}
{/* Ceiling members deliberately off-square */}
{bays
.filter((_, i) => i % 3 === 0)
.map((z, i) => (
))}
{(['left', 'right', 'back'] as const).map((wall) => (
))}
);
}
/**
* Cubist studio — Cubism.
*
* The Bateau-Lavoir atelier: slim cast-iron columns in the corners, riveted tie
* rods across the room, and a cornice broken into faceted plaster planes — the
* building itself analysed into flat facets.
*/
export function CubistAtelierDetails({ room, trim }: PeriodProps) {
const corners = useCorners(room.halfW, room.halfD, 0.24);
const ties = useBayZ(room.depth, room.halfD, 3.6, 8);
const iron = '#4d4a45';
const facet = '#dedad2';
const facets = useRepeat(room.depth, 1.15, 22);
return (
{/* Cast-iron columns in the corner pockets */}
{corners.map(([x, z], i) => (
))}
{/* Riveted tie rods spanning the room, with a turnbuckle at mid-span */}
{ties.map((z, i) => (
))}
{/* Faceted cornice: flat planes tilted alternately */}
{(['left', 'right'] as const).map((wall) =>
facets.map((z, i) => (
))
)}
{(['left', 'right', 'back'] as const).map((wall) => (
))}
);
}
/**
* Futurist loft — Futurism.
*
* Sant'Elia's Città Nuova reduced to one room: riveted steel stanchions,
* diagonal wind bracing across the upper wall, and a gantry rail running the
* length of the hall. Structure celebrated, nothing concealed.
*/
export function FuturistDetails({ room, trim }: PeriodProps) {
const bays = useBayZ(room.depth, room.halfD, 4.0, 8);
const steel = '#8f9298';
const dark = shade(steel, -0.42);
return (
{/* Riveted stanchions */}
{bays.map((z, i) => (
{[-1, 1].map((side) => (
{[0.6, 1.7, 2.8, 3.8].map((y) => (
))}
))}
))}
{/* Diagonal wind bracing between the stanchions, above the hang */}
{bays.slice(0, -1).map((z, i) => {
const mid = (z + bays[i + 1]) / 2;
const run = bays[i + 1] - z;
return (
{[-1, 1].map((side) =>
[-1, 1].map((dir) => (
))
)}
);
})}
{/* Gantry rail and its brackets */}
{(['left', 'right', 'back'] as const).map((wall) => (
))}
);
}
/**
* Suprematist white cube — Suprematism.
*
* The 0,10 room: a bare white shell, a shelf slung across the upper corner
* where Malevich hung the Black Square in the icon position, and thin black and
* red geometric bands drifting off the axis of the walls.
*/
export function SuprematistDetails({ room, trim }: PeriodProps) {
const bars = useMemo(() => {
const seeds = [-0.62, -0.24, 0.18, 0.55];
return seeds.map((t, i) => ({
z: t,
y: 3.0 + (i % 3) * 0.28,
len: 0.9 + (i % 2) * 0.7,
tilt: i % 2 === 0 ? 0.22 : -0.34,
color: i % 3 === 0 ? '#c8302a' : '#141414',
}));
}, []);
return (
{/* Icon-corner shelf, carried on two plain brackets */}
{[-0.4, 0.4].map((t) => (
))}
{/* Drifting bands above the hang */}
{(['left', 'right'] as const).map((wall) =>
bars.map((b, i) => (
))
)}
{/* Shadow-gap reveal instead of a cornice */}
{(['left', 'right', 'back'] as const).map((wall) => (
))}
);
}
/**
* Constructivist space — Constructivism.
*
* Rodchenko's lattice made architecture: red-painted diagonal trusses under the
* ceiling, angled Proun panels folded into the corners, and exposed steel ties.
* Every member is doing structural work and says so.
*/
export function ConstructivistDetails({ room, trim }: PeriodProps) {
const bays = useBayZ(room.depth, room.halfD, 2.8, 10);
const corners = useCorners(room.halfW, room.halfD, 0.3);
const red = '#b8342a';
const steel = '#9a9a96';
return (
{/* Lattice truss under the ceiling — top chord, bottom chord, diagonals */}
{bays.map((z, i) =>
[-0.9, 0.9].map((x) => (
))
)}
{/* Proun panels folded into the corners */}
{corners.map(([x, z], i) => (
))}
{/* Steel tie rails at the hang line and the skirting */}
{(['left', 'right', 'back'] as const).map((wall) => (
))}
);
}
/**
* Dada salon — Dada.
*
* A bourgeois room being dismantled: cornice fragments that stop and restart at
* different heights, a picture rail hung visibly out of level, and a service
* pipe left running across the wall where the moulding should be.
*/
export function DadaDetails({ room, trim }: PeriodProps) {
const fragments = useMemo(
() =>
[
{ wall: 'left' as const, y: 4.04, span: 0.3, proud: 0.14 },
{ wall: 'left' as const, y: 3.78, span: 0.18, proud: 0.1 },
{ wall: 'right' as const, y: 4.12, span: 0.44, proud: 0.16 },
{ wall: 'right' as const, y: 3.9, span: 0.22, proud: 0.09 },
{ wall: 'back' as const, y: 4.06, span: 0.35, proud: 0.13 },
],
[]
);
const plaster = '#e6dac4';
const pipe = '#6a6259';
return (
{/* Cornice, in pieces */}
{fragments.map((f, i) => (
))}
{/* Picture rail, visibly out of level */}
{[-1, 1].map((side) => (
))}
{/* Service pipe run, with a clumsy elbow into the ceiling */}
{/* Skirting that changes its mind halfway along */}
);
}
/**
* Surrealist interior — Surrealism.
*
* The 1938 Exposition Internationale: sacks slung under the ceiling, drapery
* pulled across the cornice, and a door standing in a corner where no door can
* lead anywhere. The room is furnished as an image, not as a gallery.
*/
export function SurrealistDetails({ room, trim }: PeriodProps) {
const sacks = useBayZ(room.depth, room.halfD, 2.6, 9);
return (
{/* Sacks hanging under the ceiling */}
{sacks.map((z, i) =>
[-1, 0, 1].map((c) => (
))
)}
{/* Drapery gathered along the top of the side walls */}
{(['left', 'right'] as const).map((wall) =>
sacks.map((z, i) => (
))
)}
{/* A door that leads nowhere, standing in the corner pocket */}
{(['left', 'right', 'back'] as const).map((wall) => (
))}
{/* A brazier glow in the opposite corner */}
);
}
/**
* NYC loft — Abstract Expressionism.
*
* A Tenth Street cast-iron building: fluted iron columns on their corbelled
* caps, a pressed-tin ceiling field, and the sprinkler main running the length
* of the room. Industrial fabric left exactly as the painters found it.
*/
export function LoftDetails({ room, trim }: PeriodProps) {
const corners = useCorners(room.halfW, room.halfD, 0.26);
const joists = useBayZ(room.depth, room.halfD, 1.7, 13);
const iron = '#5c5852';
return (
{/* Cast-iron columns with corbelled caps */}
{corners.map(([x, z], i) => (
))}
{/* Pressed-tin ceiling field on close-set joists */}
{/* Sprinkler main with drops */}
{joists
.filter((_, i) => i % 4 === 0)
.map((z, i) => (
))}
{/* Steel skirting angle */}
{(['left', 'right', 'back'] as const).map((wall) => (
))}
);
}
/**
* Pop Art gallery — Pop Art.
*
* The 1960s dealer's room: a flat white shell, recessed fluorescent troffers in
* a ceiling grid, an aluminium reveal at the floor, and a shadow gap where a
* cornice used to be. The architecture is engineered to disappear.
*/
export function WhiteCubeDetails({ room, trim }: PeriodProps) {
const troffers = useMemo(() => {
const cols = Math.max(2, Math.min(3, Math.round(room.width / 4.5)));
const rows = Math.max(2, Math.min(6, Math.round(room.depth / 3.4)));
const out: [number, number][] = [];
for (let c = 0; c < cols; c++) {
for (let r = 0; r < rows; r++) {
out.push([
-room.halfW + (room.width / (cols + 1)) * (c + 1),
-room.halfD + (room.depth / (rows + 1)) * (r + 1),
]);
}
}
return out;
}, [room.width, room.depth, room.halfW, room.halfD]);
return (
{/* Recessed fluorescent troffers */}
{troffers.map(([x, z], i) => (
))}
{/* Ceiling grid tees between the fittings */}
z).filter((z, i, a) => a.indexOf(z) === i)}
axis="x"
width={0.05}
height={0.04}
y={4.1}
color="#d6d6d6"
roughness={0.5}
metalness={0.36}
/>
{/* Shadow gap and aluminium skirting reveal */}
{(['left', 'right', 'back'] as const).map((wall) => (
))}
);
}