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) => ( ))} ); }