638 lines
26 KiB
TypeScript
638 lines
26 KiB
TypeScript
import type { ArtMovement } from '../types';
|
||
import type { SurfaceTextureKind } from '../utils/galleryProceduralTextures';
|
||
|
||
export type GalleryWindowStyle =
|
||
| 'roman-arch'
|
||
| 'gothic-lancet'
|
||
| 'baroque-pair'
|
||
| 'sash'
|
||
| 'factory'
|
||
| 'skylight'
|
||
| 'art-nouveau'
|
||
| 'glass-block'
|
||
| 'clerestory'
|
||
| 'round-oculus';
|
||
|
||
export interface GalleryWindowSpec {
|
||
wall: 'back' | 'left' | 'right' | 'ceiling';
|
||
/** Offset along wall axis from center (meters). */
|
||
x: number;
|
||
/** Vertical center height (meters). */
|
||
y: number;
|
||
width: number;
|
||
height: number;
|
||
style: GalleryWindowStyle;
|
||
lightColor: string;
|
||
lightIntensity: number;
|
||
}
|
||
|
||
export interface MovementInteriorStyle {
|
||
id: string;
|
||
label: string;
|
||
subtitle: string;
|
||
surfaces: {
|
||
wall: SurfaceTextureKind;
|
||
wallSide?: SurfaceTextureKind;
|
||
ceiling: SurfaceTextureKind;
|
||
floor: SurfaceTextureKind;
|
||
};
|
||
tints: {
|
||
wall: string;
|
||
wallSide?: string;
|
||
ceiling: string;
|
||
floor: string;
|
||
trim: string;
|
||
};
|
||
titleColor: string;
|
||
ambient: number;
|
||
warmLight: string;
|
||
sunLight: string;
|
||
fog: string;
|
||
background: string;
|
||
doorWood: [string, string, string];
|
||
windows: GalleryWindowSpec[];
|
||
trackLights: number;
|
||
details: 'palazzo' | 'baroque' | 'medieval' | 'neoclassical' | 'salon' | 'modern' | 'classical' | 'museum' | 'industrial' | 'atelier';
|
||
}
|
||
|
||
function windows(...specs: GalleryWindowSpec[]): GalleryWindowSpec[] {
|
||
return specs;
|
||
}
|
||
|
||
function mk(
|
||
id: string,
|
||
label: string,
|
||
subtitle: string,
|
||
surfaces: MovementInteriorStyle['surfaces'],
|
||
tints: MovementInteriorStyle['tints'],
|
||
opts: Partial<Omit<MovementInteriorStyle, 'id' | 'label' | 'subtitle' | 'surfaces' | 'tints'>> & {
|
||
details: MovementInteriorStyle['details'];
|
||
windows: GalleryWindowSpec[];
|
||
}
|
||
): MovementInteriorStyle {
|
||
return {
|
||
id,
|
||
label,
|
||
subtitle,
|
||
surfaces,
|
||
tints,
|
||
titleColor: opts.titleColor ?? '#4a3020',
|
||
ambient: opts.ambient ?? 0.55,
|
||
warmLight: opts.warmLight ?? '#fff4e8',
|
||
sunLight: opts.sunLight ?? '#fffaf0',
|
||
fog: opts.fog ?? '#1a1814',
|
||
background: opts.background ?? '#121010',
|
||
doorWood: opts.doorWood ?? ['#3d2818', '#4e3624', '#261a10'],
|
||
windows: opts.windows,
|
||
trackLights: opts.trackLights ?? 0.8,
|
||
details: opts.details,
|
||
};
|
||
}
|
||
|
||
/** Unique photo-real interior per movement (keyed by database id). */
|
||
const BY_MOVEMENT_ID: Record<number, MovementInteriorStyle> = {
|
||
27: mk(
|
||
'ancient-classical',
|
||
'Roman atrium',
|
||
'Marble-clad villa · mosaic floor · clerestory daylight',
|
||
{ wall: 'limestone', ceiling: 'plaster-warm', floor: 'mosaic-roman' },
|
||
{ wall: '#e8e0d0', ceiling: '#f5f0e8', floor: '#c8b898', trim: '#a89878' },
|
||
{
|
||
details: 'classical',
|
||
titleColor: '#5a4830',
|
||
ambient: 0.62,
|
||
warmLight: '#fff8e8',
|
||
windows: windows(
|
||
{ wall: 'back', x: -2.5, y: 3.2, width: 1.4, height: 1.8, style: 'roman-arch', lightColor: '#fff8e0', lightIntensity: 3.2 },
|
||
{ wall: 'back', x: 2.5, y: 3.2, width: 1.4, height: 1.8, style: 'roman-arch', lightColor: '#fff8e0', lightIntensity: 3.2 },
|
||
{ wall: 'left', x: 0, y: 3.0, width: 2.0, height: 1.6, style: 'clerestory', lightColor: '#fffaf0', lightIntensity: 2.8 },
|
||
{ wall: 'right', x: 0, y: 3.0, width: 2.0, height: 1.6, style: 'clerestory', lightColor: '#fffaf0', lightIntensity: 2.8 }
|
||
),
|
||
trackLights: 0.4,
|
||
doorWood: ['#6a5840', '#7a6848', '#5a4830'],
|
||
}
|
||
),
|
||
28: mk(
|
||
'byzantine-sanctuary',
|
||
'Byzantine chapel',
|
||
'Gold mosaic walls · amber light through arched windows',
|
||
{ wall: 'mosaic-byzantine', ceiling: 'gilded-stucco', floor: 'marble-checker' },
|
||
{ wall: '#d4af37', ceiling: '#c8a840', floor: '#ddd8cc', trim: '#b8860b' },
|
||
{
|
||
details: 'medieval',
|
||
titleColor: '#f0e8c0',
|
||
ambient: 0.48,
|
||
warmLight: '#ffd898',
|
||
sunLight: '#ffe8b0',
|
||
fog: '#0a0806',
|
||
background: '#060504',
|
||
windows: windows(
|
||
{ wall: 'back', x: 0, y: 2.8, width: 1.0, height: 2.2, style: 'roman-arch', lightColor: '#ffb860', lightIntensity: 2.4 },
|
||
{ wall: 'left', x: -1, y: 2.6, width: 0.7, height: 1.8, style: 'roman-arch', lightColor: '#ffb860', lightIntensity: 1.8 },
|
||
{ wall: 'right', x: 1, y: 2.6, width: 0.7, height: 1.8, style: 'roman-arch', lightColor: '#ffb860', lightIntensity: 1.8 }
|
||
),
|
||
trackLights: 0.5,
|
||
doorWood: ['#3a3020', '#4a3830', '#2a2018'],
|
||
}
|
||
),
|
||
29: mk(
|
||
'gothic-cathedral',
|
||
'Gothic hall',
|
||
'Stone vault · tall lancet windows · flagstone floor',
|
||
{ wall: 'rough-stone', ceiling: 'basalt', floor: 'flagstone' },
|
||
{ wall: '#8a8478', ceiling: '#3a3630', floor: '#6a6458', trim: '#5c5648' },
|
||
{
|
||
details: 'medieval',
|
||
titleColor: '#e8dcc8',
|
||
ambient: 0.52,
|
||
warmLight: '#e8d8c0',
|
||
sunLight: '#d0e8ff',
|
||
fog: '#0c0c10',
|
||
windows: windows(
|
||
{ wall: 'back', x: -2, y: 2.8, width: 0.8, height: 2.8, style: 'gothic-lancet', lightColor: '#c8e0ff', lightIntensity: 3.5 },
|
||
{ wall: 'back', x: 2, y: 2.8, width: 0.8, height: 2.8, style: 'gothic-lancet', lightColor: '#c8e0ff', lightIntensity: 3.5 },
|
||
{ wall: 'left', x: 0, y: 3.0, width: 0.9, height: 2.6, style: 'gothic-lancet', lightColor: '#c8e0ff', lightIntensity: 2.8 },
|
||
{ wall: 'right', x: 0, y: 3.0, width: 0.9, height: 2.6, style: 'gothic-lancet', lightColor: '#c8e0ff', lightIntensity: 2.8 }
|
||
),
|
||
trackLights: 0.3,
|
||
}
|
||
),
|
||
30: mk(
|
||
'early-renaissance-palazzo',
|
||
'Florentine palazzo',
|
||
'Lime-washed walls · terracotta accents · arched windows',
|
||
{ wall: 'painted-lime', wallSide: 'stucco-cream', ceiling: 'fresco-worn', floor: 'terracotta-tiles' },
|
||
{ wall: '#f4ebe0', ceiling: '#faf6ee', floor: '#c89070', trim: '#c9a227' },
|
||
{
|
||
details: 'palazzo',
|
||
titleColor: '#6b4423',
|
||
windows: windows(
|
||
{ wall: 'back', x: -2.2, y: 2.6, width: 1.3, height: 1.7, style: 'roman-arch', lightColor: '#fff4e0', lightIntensity: 3.0 },
|
||
{ wall: 'back', x: 2.2, y: 2.6, width: 1.3, height: 1.7, style: 'roman-arch', lightColor: '#fff4e0', lightIntensity: 3.0 },
|
||
{ wall: 'left', x: 0, y: 2.8, width: 2.2, height: 1.4, style: 'clerestory', lightColor: '#fffaf0', lightIntensity: 2.6 }
|
||
),
|
||
}
|
||
),
|
||
31: mk(
|
||
'high-renaissance-palazzo',
|
||
'Roman palazzo',
|
||
'Marble and stucco · coffered ceiling · checker floor',
|
||
{ wall: 'marble-veined-carrara', wallSide: 'stucco-cream', ceiling: 'plaster-warm', floor: 'marble-checker' },
|
||
{ wall: '#f0ece4', wallSide: '#efe4d4', ceiling: '#faf6ee', floor: '#ddd8cc', trim: '#c9a227' },
|
||
{
|
||
details: 'palazzo',
|
||
titleColor: '#6b4423',
|
||
windows: windows(
|
||
{ wall: 'back', x: 0, y: 2.8, width: 2.8, height: 1.8, style: 'baroque-pair', lightColor: '#fff8f0', lightIntensity: 3.8 },
|
||
{ wall: 'left', x: 0, y: 3.0, width: 2.0, height: 1.5, style: 'clerestory', lightColor: '#fffaf0', lightIntensity: 2.8 },
|
||
{ wall: 'right', x: 0, y: 3.0, width: 2.0, height: 1.5, style: 'clerestory', lightColor: '#fffaf0', lightIntensity: 2.8 },
|
||
{ wall: 'ceiling', x: 0, y: 0, width: 3.0, height: 2.0, style: 'round-oculus', lightColor: '#ffffff', lightIntensity: 4.0 }
|
||
),
|
||
}
|
||
),
|
||
32: mk(
|
||
'northern-renaissance-hall',
|
||
'Flemish panel hall',
|
||
'Oak wainscoting · leaded glass · herringbone floor',
|
||
{ wall: 'oak-panel', wallSide: 'plaster-warm', ceiling: 'dark-wood-panel', floor: 'parquet-herringbone' },
|
||
{ wall: '#8a6848', wallSide: '#f0ebe3', ceiling: '#3a2818', floor: '#8a6848', trim: '#5c4030' },
|
||
{
|
||
details: 'salon',
|
||
titleColor: '#f0e8d8',
|
||
warmLight: '#ffe8c8',
|
||
windows: windows(
|
||
{ wall: 'back', x: -2, y: 2.5, width: 1.2, height: 1.5, style: 'sash', lightColor: '#e8f0ff', lightIntensity: 2.8 },
|
||
{ wall: 'back', x: 2, y: 2.5, width: 1.2, height: 1.5, style: 'sash', lightColor: '#e8f0ff', lightIntensity: 2.8 },
|
||
{ wall: 'left', x: 0, y: 2.6, width: 1.8, height: 1.4, style: 'sash', lightColor: '#e8f0ff', lightIntensity: 2.4 }
|
||
),
|
||
doorWood: ['#4a3020', '#5c3a28', '#3a2010'],
|
||
}
|
||
),
|
||
33: mk(
|
||
'mannerist-villa',
|
||
'Mannerist gallery',
|
||
'Dramatic stucco · elongated windows · veined marble',
|
||
{ wall: 'stucco-terracotta', ceiling: 'gilded-stucco', floor: 'marble-veined-emerald' },
|
||
{ wall: '#d8b898', ceiling: '#d8c070', floor: '#dce8e0', trim: '#b8860b' },
|
||
{
|
||
details: 'baroque',
|
||
titleColor: '#4a2818',
|
||
windows: windows(
|
||
{ wall: 'back', x: -1.8, y: 2.7, width: 1.0, height: 2.2, style: 'roman-arch', lightColor: '#fff0d8', lightIntensity: 2.8 },
|
||
{ wall: 'back', x: 1.8, y: 2.7, width: 1.0, height: 2.2, style: 'roman-arch', lightColor: '#fff0d8', lightIntensity: 2.8 },
|
||
{ wall: 'right', x: 0, y: 3.0, width: 1.6, height: 1.2, style: 'clerestory', lightColor: '#fffaf0', lightIntensity: 2.2 }
|
||
),
|
||
}
|
||
),
|
||
34: mk(
|
||
'baroque-palace',
|
||
'Baroque state gallery',
|
||
'Crimson velvet · gilded stucco · grand windows',
|
||
{ wall: 'velvet-crimson', ceiling: 'gilded-stucco', floor: 'marble-veined-carrara' },
|
||
{ wall: '#5c1828', ceiling: '#d8c070', floor: '#ece8e0', trim: '#d4af37' },
|
||
{
|
||
details: 'baroque',
|
||
titleColor: '#f0d890',
|
||
ambient: 0.58,
|
||
warmLight: '#ffd898',
|
||
fog: '#100808',
|
||
windows: windows(
|
||
{ wall: 'back', x: 0, y: 2.6, width: 3.2, height: 2.2, style: 'baroque-pair', lightColor: '#fff8e8', lightIntensity: 4.5 },
|
||
{ wall: 'left', x: 0, y: 2.8, width: 1.8, height: 1.8, style: 'baroque-pair', lightColor: '#fff8e8', lightIntensity: 3.2 },
|
||
{ wall: 'right', x: 0, y: 2.8, width: 1.8, height: 1.8, style: 'baroque-pair', lightColor: '#fff8e8', lightIntensity: 3.2 },
|
||
{ wall: 'ceiling', x: 0, y: 0, width: 2.5, height: 1.8, style: 'skylight', lightColor: '#ffffff', lightIntensity: 3.5 }
|
||
),
|
||
trackLights: 0.6,
|
||
doorWood: ['#1a1008', '#2a1810', '#120c06'],
|
||
}
|
||
),
|
||
35: mk(
|
||
'rococo-salon',
|
||
'Rococo salon',
|
||
'Pastel painted walls · gilt trim · chevron parquet',
|
||
{ wall: 'painted-oil-satin', ceiling: 'silk-pale', floor: 'parquet-chevrons' },
|
||
{ wall: '#e8dce8', ceiling: '#faf6f0', floor: '#c8a882', trim: '#d4af37' },
|
||
{
|
||
details: 'salon',
|
||
titleColor: '#6a4858',
|
||
warmLight: '#fff0f0',
|
||
windows: windows(
|
||
{ wall: 'back', x: -2, y: 2.5, width: 1.4, height: 1.8, style: 'baroque-pair', lightColor: '#fff8ff', lightIntensity: 3.4 },
|
||
{ wall: 'back', x: 2, y: 2.5, width: 1.4, height: 1.8, style: 'baroque-pair', lightColor: '#fff8ff', lightIntensity: 3.4 },
|
||
{ wall: 'ceiling', x: 0, y: 0, width: 2.0, height: 1.5, style: 'skylight', lightColor: '#ffffff', lightIntensity: 3.0 }
|
||
),
|
||
}
|
||
),
|
||
36: mk(
|
||
'neoclassical-museum',
|
||
'Neoclassical museum',
|
||
'White painted walls · coffered ceiling · skylit salon',
|
||
{ wall: 'painted-flat', ceiling: 'plaster-white', floor: 'marble-veined-carrara' },
|
||
{ wall: '#f2f0ec', ceiling: '#fafafa', floor: '#eceae6', trim: '#b8b0a4' },
|
||
{
|
||
details: 'neoclassical',
|
||
titleColor: '#4a4844',
|
||
ambient: 0.65,
|
||
windows: windows(
|
||
{ wall: 'back', x: 0, y: 2.6, width: 2.8, height: 2.0, style: 'baroque-pair', lightColor: '#ffffff', lightIntensity: 4.0 },
|
||
{ wall: 'ceiling', x: 0, y: 0, width: 4.0, height: 2.5, style: 'skylight', lightColor: '#ffffff', lightIntensity: 5.0 },
|
||
{ wall: 'left', x: 0, y: 3.0, width: 2.0, height: 1.2, style: 'clerestory', lightColor: '#fffaf0', lightIntensity: 2.5 }
|
||
),
|
||
trackLights: 0.7,
|
||
}
|
||
),
|
||
37: mk(
|
||
'romantic-gothic-revival',
|
||
'Romantic gallery',
|
||
'Dark walnut paneling · pointed windows · Persian carpet tones',
|
||
{ wall: 'walnut-panel', ceiling: 'dark-plaster', floor: 'parquet-herringbone' },
|
||
{ wall: '#5a4030', ceiling: '#2a2420', floor: '#6a5040', trim: '#8a7050' },
|
||
{
|
||
details: 'salon',
|
||
titleColor: '#e8dcc8',
|
||
ambient: 0.5,
|
||
warmLight: '#ffe0c0',
|
||
windows: windows(
|
||
{ wall: 'back', x: -1.5, y: 2.6, width: 0.9, height: 2.2, style: 'gothic-lancet', lightColor: '#c8d8ff', lightIntensity: 2.6 },
|
||
{ wall: 'back', x: 1.5, y: 2.6, width: 0.9, height: 2.2, style: 'gothic-lancet', lightColor: '#c8d8ff', lightIntensity: 2.6 },
|
||
{ wall: 'right', x: 0, y: 2.5, width: 1.4, height: 1.6, style: 'sash', lightColor: '#e8f0ff', lightIntensity: 2.2 }
|
||
),
|
||
}
|
||
),
|
||
38: mk(
|
||
'realist-bourgeois',
|
||
'Realist picture gallery',
|
||
'Warm painted walls · bourgeois salon · oak parquet',
|
||
{ wall: 'painted-emulsion', ceiling: 'painted-emulsion', floor: 'parquet-herringbone' },
|
||
{ wall: '#e8e2d8', ceiling: '#f5f0e8', floor: '#a08060', trim: '#8a7050' },
|
||
{
|
||
details: 'salon',
|
||
windows: windows(
|
||
{ wall: 'back', x: 0, y: 2.5, width: 2.4, height: 1.6, style: 'sash', lightColor: '#f0f4ff', lightIntensity: 3.2 },
|
||
{ wall: 'left', x: 0, y: 2.7, width: 1.6, height: 1.3, style: 'sash', lightColor: '#f0f4ff', lightIntensity: 2.4 }
|
||
),
|
||
}
|
||
),
|
||
39: mk(
|
||
'impressionist-salon',
|
||
'Impressionist salon',
|
||
'North-light skylight · pale painted walls · herringbone floor',
|
||
{ wall: 'painted-emulsion', ceiling: 'painted-flat', floor: 'parquet-herringbone' },
|
||
{ wall: '#f0ebe3', ceiling: '#fafafa', floor: '#c8a882', trim: '#c9a96e' },
|
||
{
|
||
details: 'salon',
|
||
ambient: 0.68,
|
||
windows: windows(
|
||
{ wall: 'ceiling', x: 0, y: 0, width: 5.0, height: 3.0, style: 'skylight', lightColor: '#ffffff', lightIntensity: 6.0 },
|
||
{ wall: 'back', x: -2, y: 2.6, width: 1.2, height: 1.4, style: 'sash', lightColor: '#f0f8ff', lightIntensity: 2.5 },
|
||
{ wall: 'back', x: 2, y: 2.6, width: 1.2, height: 1.4, style: 'sash', lightColor: '#f0f8ff', lightIntensity: 2.5 }
|
||
),
|
||
trackLights: 0.5,
|
||
}
|
||
),
|
||
40: mk(
|
||
'post-impressionist-atelier',
|
||
'Montmartre atelier',
|
||
'Painted studio walls · large north window · worn floorboards',
|
||
{ wall: 'painted-emulsion', ceiling: 'painted-emulsion', floor: 'parquet-herringbone' },
|
||
{ wall: '#e8e0d0', ceiling: '#f0ebe0', floor: '#9a7858', trim: '#8a6848' },
|
||
{
|
||
details: 'atelier',
|
||
windows: windows(
|
||
{ wall: 'left', x: 0, y: 2.4, width: 2.8, height: 2.0, style: 'factory', lightColor: '#f0f8ff', lightIntensity: 4.5 },
|
||
{ wall: 'back', x: 0, y: 2.8, width: 1.0, height: 1.2, style: 'sash', lightColor: '#f0f4ff', lightIntensity: 2.0 }
|
||
),
|
||
}
|
||
),
|
||
41: mk(
|
||
'symbolist-chamber',
|
||
'Symbolist chamber',
|
||
'Deep green painted walls · amber window glow · dark parquet',
|
||
{ wall: 'painted-oil-matte', ceiling: 'dark-plaster', floor: 'parquet-herringbone' },
|
||
{ wall: '#1a4030', ceiling: '#1a1814', floor: '#4a3828', trim: '#8a7050' },
|
||
{
|
||
details: 'salon',
|
||
titleColor: '#d8e8c8',
|
||
ambient: 0.55,
|
||
warmLight: '#ffd898',
|
||
windows: windows(
|
||
{ wall: 'back', x: 0, y: 2.4, width: 1.0, height: 1.8, style: 'sash', lightColor: '#ffb860', lightIntensity: 2.0 },
|
||
{ wall: 'right', x: 0, y: 2.6, width: 0.8, height: 1.4, style: 'gothic-lancet', lightColor: '#ffd080', lightIntensity: 1.6 }
|
||
),
|
||
trackLights: 0.9,
|
||
}
|
||
),
|
||
42: mk(
|
||
'art-nouveau-salon',
|
||
'Art Nouveau salon',
|
||
'Painted plaster · stained glass · terrazzo floor',
|
||
{ wall: 'painted-oil-satin', ceiling: 'silk-gold', floor: 'terrazzo' },
|
||
{ wall: '#f0ece4', ceiling: '#d8c890', floor: '#d8d0c4', trim: '#6a9868' },
|
||
{
|
||
details: 'salon',
|
||
titleColor: '#3a5840',
|
||
windows: windows(
|
||
{ wall: 'back', x: 0, y: 2.5, width: 2.2, height: 2.0, style: 'art-nouveau', lightColor: '#e8ffe8', lightIntensity: 3.2 },
|
||
{ wall: 'left', x: 0, y: 2.6, width: 1.4, height: 1.8, style: 'art-nouveau', lightColor: '#ffe8f0', lightIntensity: 2.4 },
|
||
{ wall: 'ceiling', x: 0, y: 0, width: 2.0, height: 1.2, style: 'skylight', lightColor: '#ffffff', lightIntensity: 2.8 }
|
||
),
|
||
}
|
||
),
|
||
43: mk(
|
||
'fauvist-studio',
|
||
'Fauvist studio',
|
||
'Bold painted walls · flooded with color and light',
|
||
{ wall: 'painted-oil-matte', wallSide: 'painted-oil-matte', ceiling: 'painted-emulsion', floor: 'parquet-herringbone' },
|
||
{ wall: '#e89060', wallSide: '#e8c860', ceiling: '#f8f0e0', floor: '#a07048', trim: '#c04020' },
|
||
{
|
||
details: 'atelier',
|
||
titleColor: '#402010',
|
||
ambient: 0.7,
|
||
windows: windows(
|
||
{ wall: 'left', x: 0, y: 2.5, width: 3.0, height: 2.2, style: 'factory', lightColor: '#fff8f0', lightIntensity: 5.0 },
|
||
{ wall: 'back', x: 0, y: 2.8, width: 1.6, height: 1.2, style: 'sash', lightColor: '#fff0e0', lightIntensity: 2.5 }
|
||
),
|
||
}
|
||
),
|
||
44: mk(
|
||
'expressionist-room',
|
||
'Expressionist room',
|
||
'Angular wood panels · dramatic raking light',
|
||
{ wall: 'dark-wood-panel', ceiling: 'dark-plaster', floor: 'parquet-herringbone' },
|
||
{ wall: '#3a2818', ceiling: '#2a2018', floor: '#5a4030', trim: '#8a6040' },
|
||
{
|
||
details: 'atelier',
|
||
titleColor: '#f0d8b0',
|
||
ambient: 0.52,
|
||
windows: windows(
|
||
{ wall: 'back', x: -1.5, y: 2.6, width: 1.0, height: 1.6, style: 'sash', lightColor: '#ffe8c0', lightIntensity: 2.8 },
|
||
{ wall: 'right', x: 0, y: 2.4, width: 2.0, height: 1.8, style: 'factory', lightColor: '#fff0d8', lightIntensity: 3.5 }
|
||
),
|
||
}
|
||
),
|
||
45: mk(
|
||
'cubist-studio',
|
||
'Cubist studio',
|
||
'Paris atelier · factory windows · painted plaster',
|
||
{ wall: 'painted-emulsion', ceiling: 'painted-flat', floor: 'parquet-herringbone' },
|
||
{ wall: '#e8e4dc', ceiling: '#f5f5f5', floor: '#9a8870', trim: '#888888' },
|
||
{
|
||
details: 'atelier',
|
||
ambient: 0.65,
|
||
windows: windows(
|
||
{ wall: 'left', x: 0, y: 2.5, width: 3.5, height: 2.4, style: 'factory', lightColor: '#f0f8ff', lightIntensity: 5.5 },
|
||
{ wall: 'back', x: 0, y: 3.0, width: 1.8, height: 1.0, style: 'clerestory', lightColor: '#ffffff', lightIntensity: 2.5 }
|
||
),
|
||
}
|
||
),
|
||
46: mk(
|
||
'futurist-loft',
|
||
'Futurist loft',
|
||
'Steel and glass · industrial concrete · sweeping daylight',
|
||
{ wall: 'concrete-raw', ceiling: 'concrete-raw', floor: 'industrial-floor' },
|
||
{ wall: '#a8a8a8', ceiling: '#989898', floor: '#888880', trim: '#606060' },
|
||
{
|
||
details: 'industrial',
|
||
titleColor: '#303030',
|
||
ambient: 0.62,
|
||
fog: '#181818',
|
||
windows: windows(
|
||
{ wall: 'back', x: 0, y: 2.6, width: 4.0, height: 2.4, style: 'factory', lightColor: '#ffffff', lightIntensity: 6.0 },
|
||
{ wall: 'left', x: 0, y: 2.8, width: 2.5, height: 1.6, style: 'factory', lightColor: '#f0f8ff', lightIntensity: 3.5 },
|
||
{ wall: 'ceiling', x: 0, y: 0, width: 3.0, height: 2.0, style: 'skylight', lightColor: '#ffffff', lightIntensity: 4.0 }
|
||
),
|
||
doorWood: ['#606060', '#707070', '#505050'],
|
||
}
|
||
),
|
||
47: mk(
|
||
'suprematist-gallery',
|
||
'Suprematist white cube',
|
||
'Matte white paint · geometric light · polished floor',
|
||
{ wall: 'painted-flat', ceiling: 'painted-flat', floor: 'concrete-polished' },
|
||
{ wall: '#ffffff', ceiling: '#ffffff', floor: '#e0e0e0', trim: '#cccccc' },
|
||
{
|
||
details: 'modern',
|
||
titleColor: '#222222',
|
||
ambient: 0.72,
|
||
fog: '#1a1a1a',
|
||
background: '#111111',
|
||
windows: windows(
|
||
{ wall: 'ceiling', x: 0, y: 0, width: 4.5, height: 3.0, style: 'skylight', lightColor: '#ffffff', lightIntensity: 6.5 },
|
||
{ wall: 'back', x: 0, y: 2.8, width: 2.0, height: 1.2, style: 'clerestory', lightColor: '#ffffff', lightIntensity: 3.0 }
|
||
),
|
||
trackLights: 0.4,
|
||
doorWood: ['#aaaaaa', '#bbbbbb', '#999999'],
|
||
}
|
||
),
|
||
48: mk(
|
||
'constructivist-space',
|
||
'Constructivist space',
|
||
'Glass block · concrete · angular daylight',
|
||
{ wall: 'concrete-block', ceiling: 'concrete-raw', floor: 'concrete-polished' },
|
||
{ wall: '#b0b0b0', ceiling: '#a0a0a0', floor: '#c0c0c0', trim: '#808080' },
|
||
{
|
||
details: 'industrial',
|
||
titleColor: '#303030',
|
||
ambient: 0.65,
|
||
windows: windows(
|
||
{ wall: 'back', x: 0, y: 2.5, width: 2.8, height: 2.0, style: 'glass-block', lightColor: '#f0f8ff', lightIntensity: 4.0 },
|
||
{ wall: 'left', x: 0, y: 2.6, width: 2.0, height: 1.8, style: 'glass-block', lightColor: '#f0f8ff', lightIntensity: 3.2 },
|
||
{ wall: 'ceiling', x: 0, y: 0, width: 2.5, height: 1.5, style: 'skylight', lightColor: '#ffffff', lightIntensity: 3.5 }
|
||
),
|
||
doorWood: ['#707070', '#808080', '#606060'],
|
||
}
|
||
),
|
||
49: mk(
|
||
'dada-salon',
|
||
'Dada salon',
|
||
'Eclectic bourgeois room · painted walls and exposed brick',
|
||
{ wall: 'painted-emulsion', wallSide: 'brick', ceiling: 'painted-emulsion', floor: 'parquet-herringbone' },
|
||
{ wall: '#e8dcc8', wallSide: '#8a5040', ceiling: '#f0ebe0', floor: '#8a6848', trim: '#6a4830' },
|
||
{
|
||
details: 'salon',
|
||
windows: windows(
|
||
{ wall: 'back', x: -1.5, y: 2.5, width: 1.0, height: 1.4, style: 'sash', lightColor: '#f0f4ff', lightIntensity: 2.5 },
|
||
{ wall: 'back', x: 1.5, y: 2.6, width: 0.8, height: 1.8, style: 'gothic-lancet', lightColor: '#ffe8c0', lightIntensity: 2.0 },
|
||
{ wall: 'right', x: 0, y: 2.4, width: 1.6, height: 1.6, style: 'factory', lightColor: '#ffffff', lightIntensity: 3.0 }
|
||
),
|
||
trackLights: 1.0,
|
||
}
|
||
),
|
||
50: mk(
|
||
'surrealist-interior',
|
||
'Surrealist interior',
|
||
'Dark painted walls · uncanny warm light',
|
||
{ wall: 'painted-oil-matte', ceiling: 'dark-plaster', floor: 'parquet-herringbone' },
|
||
{ wall: '#1a2848', ceiling: '#2a2428', floor: '#5a4838', trim: '#8a7050' },
|
||
{
|
||
details: 'salon',
|
||
titleColor: '#e8dcc8',
|
||
ambient: 0.58,
|
||
warmLight: '#ffd898',
|
||
windows: windows(
|
||
{ wall: 'back', x: 0, y: 2.5, width: 1.4, height: 1.6, style: 'sash', lightColor: '#ffe8c8', lightIntensity: 2.8 },
|
||
{ wall: 'left', x: 0, y: 2.7, width: 1.0, height: 1.2, style: 'sash', lightColor: '#c8e0ff', lightIntensity: 2.0 },
|
||
{ wall: 'ceiling', x: 0, y: 0, width: 1.5, height: 1.0, style: 'skylight', lightColor: '#ffffff', lightIntensity: 2.5 }
|
||
),
|
||
trackLights: 0.85,
|
||
}
|
||
),
|
||
51: mk(
|
||
'abstract-expressionist-loft',
|
||
'NYC loft',
|
||
'Raw brick and painted plaster · north-light factory windows',
|
||
{ wall: 'brick', wallSide: 'painted-flat', ceiling: 'concrete-raw', floor: 'industrial-floor' },
|
||
{ wall: '#8a5040', wallSide: '#f5f5f5', ceiling: '#989898', floor: '#888880', trim: '#606060' },
|
||
{
|
||
details: 'industrial',
|
||
titleColor: '#303030',
|
||
ambient: 0.65,
|
||
windows: windows(
|
||
{ wall: 'left', x: 0, y: 2.5, width: 4.0, height: 2.6, style: 'factory', lightColor: '#f0f8ff', lightIntensity: 6.5 },
|
||
{ wall: 'ceiling', x: 0, y: 0, width: 3.5, height: 2.0, style: 'skylight', lightColor: '#ffffff', lightIntensity: 4.5 }
|
||
),
|
||
}
|
||
),
|
||
52: mk(
|
||
'pop-art-gallery',
|
||
'Pop Art gallery',
|
||
'White painted walls · fluorescent daylight · polished concrete',
|
||
{ wall: 'painted-flat', ceiling: 'painted-flat', floor: 'concrete-polished' },
|
||
{ wall: '#ffffff', ceiling: '#ffffff', floor: '#d8d8d8', trim: '#cccccc' },
|
||
{
|
||
details: 'modern',
|
||
titleColor: '#222222',
|
||
ambient: 0.75,
|
||
warmLight: '#ffffff',
|
||
fog: '#1a1a1a',
|
||
background: '#101010',
|
||
windows: windows(
|
||
{ wall: 'back', x: 0, y: 2.6, width: 3.0, height: 2.0, style: 'factory', lightColor: '#ffffff', lightIntensity: 5.5 },
|
||
{ wall: 'ceiling', x: 0, y: 0, width: 5.0, height: 3.0, style: 'skylight', lightColor: '#ffffff', lightIntensity: 6.0 }
|
||
),
|
||
trackLights: 0.6,
|
||
doorWood: ['#888888', '#999999', '#777777'],
|
||
}
|
||
),
|
||
};
|
||
|
||
function blendAccent(style: MovementInteriorStyle, accentHex?: string): MovementInteriorStyle {
|
||
if (!accentHex) return style;
|
||
const w = 0.08;
|
||
const mix = (a: string, b: string) => {
|
||
const pa = parseInt(a.replace('#', ''), 16);
|
||
const pb = parseInt(b.replace('#', ''), 16);
|
||
const r = Math.round(((pa >> 16) & 255) * w + ((pb >> 16) & 255) * (1 - w));
|
||
const g = Math.round(((pa >> 8) & 255) * w + ((pb >> 8) & 255) * (1 - w));
|
||
const bl = Math.round((pa & 255) * w + (pb & 255) * (1 - w));
|
||
return `#${((r << 16) | (g << 8) | bl).toString(16).padStart(6, '0')}`;
|
||
};
|
||
return {
|
||
...style,
|
||
tints: {
|
||
...style.tints,
|
||
wall: mix(accentHex, style.tints.wall),
|
||
wallSide: style.tints.wallSide ? mix(accentHex, style.tints.wallSide) : undefined,
|
||
trim: mix(accentHex, style.tints.trim),
|
||
},
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Styles were authored with legacy ids 27–52; gallery_dev uses 1–26.
|
||
* Northern (5) / High Renaissance (6) are swapped vs the legacy sequence.
|
||
*/
|
||
const DB_ID_TO_STYLE_KEY: Record<number, number> = {
|
||
1: 27,
|
||
2: 28,
|
||
3: 29,
|
||
4: 30,
|
||
5: 32,
|
||
6: 31,
|
||
7: 33,
|
||
8: 34,
|
||
9: 35,
|
||
10: 36,
|
||
11: 37,
|
||
12: 38,
|
||
13: 39,
|
||
14: 40,
|
||
15: 41,
|
||
16: 42,
|
||
17: 43,
|
||
18: 44,
|
||
19: 45,
|
||
20: 46,
|
||
21: 47,
|
||
22: 48,
|
||
23: 49,
|
||
24: 50,
|
||
25: 51,
|
||
26: 52,
|
||
};
|
||
|
||
/** Fallback name-based resolver for movements not in the catalog. */
|
||
function fallbackByName(movement: ArtMovement & { era_name?: string }): MovementInteriorStyle {
|
||
const n = movement.name.toLowerCase();
|
||
const era = (movement.era_name ?? '').toLowerCase();
|
||
if (n.includes('byzantine')) return BY_MOVEMENT_ID[28];
|
||
if (n.includes('gothic')) return BY_MOVEMENT_ID[29];
|
||
if (n.includes('renaissance')) return BY_MOVEMENT_ID[31];
|
||
if (n.includes('baroque') || n.includes('rococo')) return BY_MOVEMENT_ID[34];
|
||
if (era.includes('medieval')) return BY_MOVEMENT_ID[29];
|
||
if (n.includes('impression')) return BY_MOVEMENT_ID[39];
|
||
if (era.includes('modern') || era.includes('contemporary')) return BY_MOVEMENT_ID[52];
|
||
return BY_MOVEMENT_ID[36];
|
||
}
|
||
|
||
export function resolveMovementInteriorStyle(
|
||
movement: ArtMovement & { era_name?: string }
|
||
): MovementInteriorStyle {
|
||
const styleKey = DB_ID_TO_STYLE_KEY[movement.id] ?? movement.id;
|
||
const base = BY_MOVEMENT_ID[styleKey] ?? fallbackByName(movement);
|
||
return blendAccent(base, movement.color);
|
||
}
|
||
|
||
/** @deprecated use surfaces.floor — kept for transitional imports */
|
||
export type GalleryFloorKind = string;
|