// Модель мира. Все координаты в метрах в локальной проекции мира: // x — на восток, y — на юг (как на экране). Углы в радианах. // Модель с самого начала рассчитана на будущую симуляцию: у всего есть id, // двери и лестницы — это связи между комнатами (граф навигации), // у объектов есть флаги «блокирует проход / блокирует обзор». export type Vec = [number, number]; export type Ring = Vec[]; export interface World { version: 1; id: string; name: string; createdAt: string; seed: number; /** Центр проекции и исходная область в градусах [south, west, north, east]. */ origin: { lat: number; lon: number }; bbox: [number, number, number, number]; /** Границы области в метрах. */ bounds: { minX: number; minY: number; maxX: number; maxY: number }; areas: Area[]; roads: Road[]; buildings: Building[]; /** Уличные объекты: деревья, машины, скамейки, фонари. */ props: Prop[]; /** Библиотека общих моделей предметов: prop.model → набор фигур. */ models: Record; attribution: string; } export type AreaKind = | 'residential' | 'commercial' | 'industrial' | 'retail' | 'farmland' | 'farmyard' | 'grass' | 'park' | 'forest' | 'scrub' | 'meadow' | 'orchard' | 'vineyard' | 'water' | 'wetland' | 'beach' | 'sand' | 'parking' | 'pitch' | 'playground' | 'cemetery' | 'construction' | 'railway' | 'school' | 'hospital' | 'pedestrian' | 'garden'; export interface Area { id: string; kind: AreaKind; /** Первое кольцо — внешнее, остальные — дыры. */ rings: Ring[]; osmId?: string; name?: string; } export type RoadKind = | 'motorway' | 'trunk' | 'primary' | 'secondary' | 'tertiary' | 'residential' | 'unclassified' | 'service' | 'living_street' | 'pedestrian' | 'track' | 'footway' | 'cycleway' | 'path' | 'steps' | 'rail' | 'tram' | 'river' | 'stream' | 'canal' | 'ditch'; export interface Road { id: string; kind: RoadKind; pts: Vec[]; /** Ширина проезжей части (или русла) в метрах. */ width: number; /** Ширина тротуара с каждой стороны, 0 — нет. */ sidewalk: number; lanes: number; oneway: boolean; layer: number; bridge: boolean; tunnel: boolean; name?: string; osmId?: string; } export type BuildingKind = | 'house' | 'apartments' | 'mixed' | 'retail' | 'restaurant' | 'office' | 'industrial' | 'warehouse' | 'school' | 'hospital' | 'hotel' | 'church' | 'civic' | 'garage' | 'shed' | 'farm'; export interface Building { id: string; osmId?: string; kind: BuildingKind; /** Подтип из OSM-тегов/POI: 'supermarket', 'bakery', 'pharmacy'… */ use?: string; name?: string; address?: string; footprint: Ring; /** Внутренние дворы (дыры в контуре). */ holes: Ring[]; /** Площадь контура, м². */ area: number; levels: number; /** Откуда взялась этажность: 'osm' | 'height' | 'estimate'. */ levelsSource: 'osm' | 'height' | 'estimate'; /** Локальная система здания: поворот сетки планировки. */ frame: { origin: Vec; angle: number }; floors: Floor[]; /** Помещения-юниты: квартиры, магазины, офисы — будущие «домохозяйства» и «рабочие места». */ units: Unit[]; /** Сколько спальных мест (оценка жильцов). */ beds: number; seed: number; } export interface Unit { id: string; kind: 'home' | 'shop' | 'office' | 'service' | 'common'; level: number; roomIds: string[]; beds: number; } export type RoomKind = | 'hall' | 'landing' | 'living' | 'kitchen' | 'bedroom' | 'bath' | 'wc' | 'storage' | 'corridor' | 'stairs' | 'lift' | 'lobby' | 'sales' | 'backroom' | 'office' | 'openoffice' | 'meeting' | 'dining' | 'prokitchen' | 'workshop' | 'hall_ind' | 'classroom' | 'staff' | 'ward' | 'guestroom' | 'nave' | 'garage' | 'utility' | 'void'; export interface Room { id: string; kind: RoomKind; /** Контур комнаты (может быть несколько колец; дыры — по чётности). Рисовать с клипом по контуру здания. */ rings: Ring[]; area: number; labelPos: Vec; unitId?: string; } export interface Wall { a: Vec; b: Vec; thickness: number; exterior: boolean; } export interface Door { id: string; a: Vec; b: Vec; /** Комнаты по обе стороны; 'outside' — улица. Это ребро графа навигации. */ rooms: [string, string]; /** 'opening' — проём без полотна (арка, выход на лестницу). */ kind: 'interior' | 'entrance' | 'unit' | 'garage' | 'opening'; /** Сторона, куда открывается полотно (точка внутри комнаты). */ swingTo: Vec; } export interface Window { a: Vec; b: Vec; roomId: string; } export interface Stairs { id: string; rings: Ring[]; /** Ось марша: от низа к верху. */ from: Vec; to: Vec; width: number; up: boolean; down: boolean; lift?: boolean; /** id комнаты-лестничной клетки на этом этаже. */ roomId: string; } export interface Floor { level: number; rooms: Room[]; walls: Wall[]; doors: Door[]; windows: Window[]; stairs: Stairs[]; props: Prop[]; } /** Примитив в локальных координатах предмета (центр предмета в 0,0; «спинка» к стене при y<0). */ export type Shape = | { t: 'rect'; x: number; y: number; w: number; h: number; r?: number; s?: ShapeStyle } | { t: 'circle'; x: number; y: number; r: number; s?: ShapeStyle } | { t: 'line'; x1: number; y1: number; x2: number; y2: number; s?: ShapeStyle }; /** Стиль: 'base' — основной предмет, 'soft' — ткань/подушка, 'dark' — техника/акцент, 'green' — растение, 'glass', 'metal'. */ export type ShapeStyle = 'base' | 'soft' | 'dark' | 'green' | 'glass' | 'metal' | 'wood' | 'water' | 'line' | 'car' | 'fabric' | 'accent'; export interface Prop { id: string; kind: string; pos: Vec; rot: number; w: number; d: number; /** Фигуры предмета; после сборки мира обычно вынесены в world.models (см. model). */ shapes?: Shape[]; model?: string; blocksMove: boolean; blocksSight: boolean; roomId?: string; /** Цвет для машин и т.п. (иначе по стилю). */ color?: string; /** Для симуляции: можно сидеть/спать/хранить и т.п. */ tags?: string[]; }