import { type ParamSchema, type SceneKindObject, type SceneKindResolveContext, type WeightedParamEntry } from "../scene/sceneKinds.js"; import { type BuildingStyle } from "./buildings.js"; import { type CityLotClass, type CityLotPiece, type CityTreeSpecies, type CityZoneBand, type CityZoneProfile } from "./cityContent.js"; import { type CircuitCorner, type CircuitGate, type CircuitSealKind } from "./raceCircuit.js"; import type { WorldSolid } from "./worldSolids.js"; import type { RoadPoint } from "./roads.js"; import { type StreetFeatureKind } from "./streetGenerator.js"; import { type CityBlockKind } from "./cityBlocks.js"; /** The editor volume kind marking a box as a procedural city district. */ export declare const CITY_KIND = "city"; /** The editor volume kind that locally overrides a city's zone band/mix — a district in a district. */ export declare const CITY_ZONE_KIND = "cityzone"; /** Fully-defaulted city params parsed from a volume's `meta`. */ export interface CityRules { /** 1 = strict orthogonal grid (regular spacing, full through-streets); 0 = organic irregular net. */ gridness: number; /** How much individual streets wander sideways. 0 = ruler-straight. */ curviness: number; /** Density of branch lanes forking off the main streets, 0..1. */ branching: number; /** How loopy the network is: 0 = a tree that dead-ends; 1 collapses to one closed circuit (a race * track). High loopiness with branching/connectivity near zero is the "race circuit" corner. */ loopiness: number; /** Extra chord edges knitting neighbors together — mesh density. 1 = a dense connected grid. */ connectivity: number; /** Fraction of dangling ends kept as cul-de-sacs (vs reconnected into loops), 0..1. */ deadEnds: number; /** Minimum curve radius (m): caps street wander curvature and corner tightness. */ minCurveRadius: number; /** Shallowest corner (degrees) a street keeps — gentler bends straighten out. */ minTurnAngle: number; /** Sharpest corner (degrees) a street may take — tighter ones are beveled away. */ maxTurnAngle: number; /** Organic boundary clipping, 0..1. 0 fills the volume's rectangle; higher grows lobes and bays. */ outline: number; /** Share of branch lanes grown as winding residential side streets, 0..1. */ residentialBranches: number; /** Road-surface relief, 0..1. 0 is dead flat; 1 is ~22 m of rise and fall across a ~520 m district. */ elevation: number; /** Steepest road grade (rise/run) the elevation pass allows on any street. */ maxGrade: number; /** Circuit space-filling, 0..1. 0 is a flowing loop around an empty middle; higher folds the lap * back through its own interior (switchbacks, hairpins, parallel runs). Circuit mode only. */ compactness: number; /** Lay parcels + buildings against the streets (city fabric). Off = bare roads/track, no plots. */ fabric: boolean; /** Streets bore through ridges on tunnel decks instead of climbing over them. */ tunnels: boolean; /** Target block size (street spacing) in meters. */ blockSize: number; /** Cross-axis spacing multiplier — 2+ gives long skinny Manhattan blocks with avenue/street rhythm. */ blockAspect: number; /** Fraction of blocks left unbuilt as intentional open space (parks/plazas/fields), 0..1. */ openSpace: number; /** Fraction of every street's frontage that carries a building, 0..1 — the compactness dial. */ roadsideOccupancy: number; /** Chance deep blocks fill their interior with a second row of buildings, 0..1. */ blockDensity: number; /** Base gap from the sidewalk edge to a lot's front, in meters (class factors scale it). */ buildingRoadSetback: number; /** Base side gap between neighboring lots, in meters (rowhouses nearly touch, farms spread). */ buildingSpacing: number; /** How strongly development clumps: junctions grow denser, density drifts in smooth waves, and * same-class runs form — 0 is uniform, 1 is strongly clustered. */ clusterStrength: number; /** Main street width in meters (boulevards/avenues render wider, lanes narrower). */ streetWidth: number; /** Share of avenues upgraded to median-divided boulevards, 0..1. */ boulevards: number; /** Branch lanes render as gravel instead of asphalt (rural roads, alleys). */ gravelLanes: boolean; /** District-wide surface: `auto` keeps asphalt mains (+ gravel lanes per `gravelLanes`); `gravel` unpaves everything. */ surface: "auto" | "gravel"; /** Zone banding: radial core-out, inverted (wealth at the rim), or spatially uniform mixing. */ profile: CityZoneProfile; /** Fraction of the district radius covered by the core band. */ coreExtent: number; /** Fraction of the district radius where the mid band ends and the edge begins. */ midExtent: number; /** Weighted building-class mix of the core band. */ coreMix: WeightedParamEntry[]; /** Weighted building-class mix of the middle band. */ midMix: WeightedParamEntry[]; /** Weighted building-class mix of the edge band. */ edgeMix: WeightedParamEntry[]; /** Global lot footprint multiplier — estates want 1.5+, dense infill wants <1. */ lotScale: number; /** Minimum building floors (district clamp over the class ranges). */ floorsMin: number; /** Maximum building floors (district clamp over the class ranges). */ floorsMax: number; /** Height of one floor in meters. */ floorHeight: number; /** Street-tree planting density, 0..1. */ treeDensity: number; /** Weighted species mix for street/park trees. */ treeMix: WeightedParamEntry[]; /** Street-light density along non-lane streets, 0..1. */ lightDensity: number; /** Parked-vehicle density along street curbs and inside parking pads, 0..1. */ parkedCars: number; /** Junction control density, 0..1: mast-arm signals on arterial crossings, sign posts below them. */ signals: number; /** Ring estate lots (mansions) with perimeter hedges and a gated gap. */ hedges: boolean; /** Connect houses/estates/farms to their street with driveways. */ driveways: boolean; /** Give commercial lots (shops, slabs) a parking pad behind the building. */ parking: boolean; /** Unbuilt non-core blocks become crop-row farm fields instead of parks. */ fields: boolean; /** Steepest ground (rise over run) a lot will build on; steeper slopes stay open cliff/canyon. */ maxSlope: number; /** Lots below this ground height are skipped — keeps buildings out of rivers/lakes/canyons floors. */ minElevation: number; /** Streets crossing ground below `minElevation` span it on a bridge deck instead of clipping. */ bridges: boolean; /** Bridge silhouette: arched deck, overhead truss, or a plain beam span. */ bridgeStyle: "arch" | "truss" | "beam"; /** Render sidewalks flanking asphalt streets. */ sidewalks: boolean; /** Sidewalk band width in meters at "street" hierarchy (boulevards scale up, lanes down). */ sidewalkWidth: number; /** Lift an instanced street race out of the district's own streets (see {@link ResolvedRace}). */ race: boolean; /** Target race lap length in meters — the search keeps the cycle closest to this. */ raceLapLength: number; /** 0 races any street; 1 keeps the lap on avenues and boulevards only. */ raceArterial: number; /** Ordered checkpoint gates placed around the lap. */ raceCheckpoints: number; /** Laps the race runs. */ raceLaps: number; /** Close every side street leaving the lap with barriers — what makes it an instanced event. */ raceSeals: boolean; /** Starting grid slots behind the line (0 = no grid). */ raceGridSlots: number; /** Radius (m) the racing line is worked toward at the lap's junction corners. */ raceCornerRadius: number; /** Building style palette id (see {@link BUILDING_STYLE_PALETTES}). */ style: BuildingStyle; /** * Name of the `BuildingKit` the near-LOD facades render with, or `""` for the palette blocks. * A game registers its bound art under this name; the document carries only the name, so a scene * stays portable and small. */ buildingKit: string; /** Seed string; same seed reproduces the same city. Empty falls back to the volume id. */ seed: string; } /** City defaults: a zoned mixed metropolis — towers downtown, slabs mid-ring, houses at the edge. */ export declare const CITY_DEFAULTS: CityRules; /** The city parameter schema — drives the inspector sliders and `meta` parse via the studio seam. */ export declare const CITY_SCHEMA: ParamSchema; /** Zone-override schema for `cityzone` volumes: pin a band and optionally a bespoke class mix. */ export declare const CITY_ZONE_SCHEMA: ParamSchema; /** Parse a volume's `meta` into fully-defaulted city rules. @internal */ export declare function readCityRules(meta: Record | undefined): CityRules; /** One synthesized street: a world-space XZ polyline with width, hierarchy level, and surface. */ export interface CityStreet { id: string; points: readonly RoadPoint[]; width: number; level: "boulevard" | "avenue" | "street" | "lane"; surface: "asphalt" | "gravel"; /** Whether sidewalk ribbons flank this street. */ sidewalk: boolean; /** Grade-limited road-surface height per point, present only when `elevation` is above 0. This is * ROAD relief, not the terrain the district stands on. */ heights?: readonly number[]; /** Cul-de-sac turning bulb at a dangling end, when the lane never reconnected. */ bulb?: RoadPoint; /** Bridge/tunnel spans along this street as `[from, to]` index windows into `points`. */ features?: readonly { kind: StreetFeatureKind; from: number; to: number; bankHeight: number; }[]; } /** One crossing of two through streets: patch center/radius plus crosswalk arm directions. */ export interface CityIntersection { id: string; x: number; z: number; /** Patch radius covering the crossing so the two ribbons never z-fight. */ radius: number; level: CityStreet["level"]; /** Outgoing arm directions (radians) with the crossing road width, for crosswalk stripes. */ arms: readonly { angle: number; width: number; }[]; } /** One building lot: footprint, zone band, class, seeded floors, massing pieces, street anchor. */ export interface CityLot { id: string; center: RoadPoint; size: readonly [number, number]; rotationY: number; floors: number; jitter: number; /** Building class the zone mix picked. */ class: CityLotClass; /** Zone band the lot fell in (after overrides). */ zone: CityZoneBand; /** Frontage point on the street centerline the lot faces. */ anchor: RoadPoint; /** Deterministic massing pieces in lot-local space (see {@link CityLotPiece}). */ pieces: readonly CityLotPiece[]; /** Id of the parcel this building stands on. */ parcel?: string; } /** One closed city block: a face of the road graph, inset to curb and land boundaries. */ export interface CityBlock { id: string; /** How the block is used: buildable, a park/plaza/field open space, or a landscaped buffer sliver. */ kind: CityBlockKind; /** Land polygon behind the sidewalk band (world XZ, closed ring). */ polygon: readonly RoadPoint[]; /** Pavement-edge polygon (curb line). The sidewalk band lies between `curb` and `polygon`. */ curb: readonly RoadPoint[]; /** Land area in m². */ area: number; } /** One street-frontage edge of a parcel. */ export interface CityParcelFrontageOut { /** Street id (`street:N`) the frontage faces. */ road: string; edgeStart: RoadPoint; edgeEnd: RoadPoint; /** Unit tangent along the frontage chord. */ tangent: RoadPoint; } /** One polygonal parcel subdivided from a block's street frontage. */ export interface CityParcel { id: string; /** Owning block id. */ block: string; polygon: readonly RoadPoint[]; /** Buildable polygon after front/side/rear setbacks ([] when setbacks consume the parcel). */ buildable: readonly RoadPoint[]; frontage: readonly CityParcelFrontageOut[]; area: number; /** Usable depth from the frontage chord to the parcel rear, meters. */ depth: number; isCorner: boolean; /** built = carries a lot; vacant = intentionally unbuilt gap; yard = leftover strip kept green. */ kind: "built" | "vacant" | "yard"; } /** One bridge deck spanning water: bank-to-bank polyline plus the silhouette style. */ export interface CityBridge { id: string; points: readonly RoadPoint[]; width: number; style: CityRules["bridgeStyle"]; /** Ground height at the banks — the deck reference the renderer drapes to. */ bankHeight: number; } /** One tunnel bore piercing a ridge: portal-to-portal polyline held at bank height. */ export interface CityTunnel { id: string; points: readonly RoadPoint[]; width: number; /** Ground height at the portals — the road floor stays flat at this height through the ridge. */ bankHeight: number; } /** One intentional open space: an unbuilt block, a block-interior courtyard, or a buffer sliver. */ export interface CityPark { id: string; center: RoadPoint; size: readonly [number, number]; rotationY: number; type: "plaza" | "green" | "meadow" | "field" | "courtyard" | "buffer"; jitter: number; /** Road-derived polygon (world XZ). Present on every block-pipeline park; the rect fields above * are its bounding proxy for coarse consumers. */ polygon?: readonly RoadPoint[]; /** Crop-row polylines for `field` parks, clipped around farm lots. */ rows?: readonly (readonly RoadPoint[])[]; } /** One placed tree: world XZ, species, and seeded scale/color jitter. */ export interface CityTree { x: number; z: number; species: CityTreeSpecies; scale: number; jitter: number; } /** One street light: curb position plus the yaw its arm faces (over the road). */ export interface CityLight { x: number; z: number; /** Yaw facing the road (engine rotationY convention). */ heading: number; } /** One hedge run: a thin box strip (estate perimeter). */ export interface CityHedge { center: RoadPoint; /** Length along the run and thickness across it. */ size: readonly [number, number]; rotationY: number; } /** One driveway ribbon from a street to a lot. */ export interface CityDriveway { points: readonly RoadPoint[]; width: number; surface: "gravel" | "pavement"; } /** One parking pad behind a commercial lot. */ export interface CityParking { center: RoadPoint; size: readonly [number, number]; rotationY: number; } /** Silhouette class of a parked vehicle — sizes the body the renderer instances. */ export type CityVehicleKind = "car" | "van" | "truck"; /** One parked vehicle standing at a curb or nose-in inside a parking pad. */ export interface CityVehicle { x: number; z: number; /** Yaw the body points down (engine rotationY convention). */ rotationY: number; kind: CityVehicleKind; /** Seeded body-paint index; the renderer maps it into its own palette. */ paint: number; /** Where it stands, so the renderer drapes it onto carriageway or pad height. */ stall: "curb" | "lot"; } /** One junction control on an approach arm: an arterial mast-arm signal, or a plain sign post. */ export interface CitySignal { x: number; z: number; /** Yaw the head faces — into the traffic approaching down this arm. */ heading: number; kind: "signal" | "sign"; /** How far the mast arm reaches over the carriageway, to the right of {@link CitySignal.heading}. */ reach: number; } /** One street closed off where it leaves the race lap: world XZ, the yaw down the sealed street, and * the width the barrier run spans. */ export interface CityRaceSeal { x: number; z: number; heading: number; width: number; kind: CircuitSealKind; } /** One kerb strip hugging a corner's road edge, as a world-space polyline to stripe. */ export interface CityRaceKerb { points: readonly RoadPoint[]; /** Strip width across, meters. */ width: number; /** Which side of travel the strip sits on: -1 left, 1 right. */ side: -1 | 1; /** Alternating stripe length along the run, meters. */ stripe: number; /** Corner this kerb belongs to (index into {@link ResolvedRace.corners}). */ corner: number; } /** The start/finish line: where it is, which way cars face, and the painted line across the road. */ export interface CityRaceStart { x: number; z: number; /** Yaw cars face on the grid (direction of travel). */ heading: number; /** The two road-edge ends of the painted line. */ line: readonly [RoadPoint, RoadPoint]; /** Arc-length position of the line along the lap, meters. */ distance: number; width: number; } /** One staggered spawn behind the start line. */ export interface CityRaceGridSlot { x: number; z: number; heading: number; } /** * An instanced street race lifted out of the district's OWN streets: a closed lap through the city, * with every side street leaving it sealed off. World space, like the rest of {@link ResolvedCity}. */ export interface ResolvedRace { /** Closed lap centerline — the last point joins the first. */ centerline: readonly RoadPoint[]; /** Road width at each centerline point, inherited from the streets the lap follows. */ widths: readonly number[]; /** Road-surface height per point, present only when the district carries `elevation`. */ heights?: readonly number[]; /** Lap length, meters. */ length: number; /** Ids of the district streets the lap runs on — style exactly these as track surface. */ streets: readonly string[]; seals: readonly CityRaceSeal[]; corners: readonly CircuitCorner[]; kerbs: readonly CityRaceKerb[]; /** Ordered gates, finish last — ready for `raceTrack({ checkpoints, laps })`. */ checkpoints: readonly CircuitGate[]; laps: number; start: CityRaceStart; grid: readonly CityRaceGridSlot[]; /** Arc-length fractions (0..1) where each timing sector begins; the first is always 0. */ sectors: readonly number[]; /** Tightest radius (m) the finished racing line actually holds. */ tightestRadius: number; } /** A resolved city district: world-space network, zoned lots, parks, and furniture. */ export interface ResolvedCity { center: readonly [number, number, number]; size: readonly [number, number]; rotationY: number; rules: CityRules; streets: readonly CityStreet[]; intersections: readonly CityIntersection[]; bridges: readonly CityBridge[]; /** Tunnel bores piercing ridges — path features on continuous streets. */ tunnels: readonly CityTunnel[]; /** Closed road-derived block polygons — the land the street network encloses. */ blocks: readonly CityBlock[]; /** Polygonal parcels subdivided from block frontage. */ parcels: readonly CityParcel[]; lots: readonly CityLot[]; parks: readonly CityPark[]; trees: readonly CityTree[]; lights: readonly CityLight[]; hedges: readonly CityHedge[]; driveways: readonly CityDriveway[]; parkingLots: readonly CityParking[]; /** Parked vehicles along the curbs and inside the parking pads. */ vehicles: readonly CityVehicle[]; /** Traffic signals and sign posts, one per controlled junction approach. */ signals: readonly CitySignal[]; /** The instanced street race through these streets, when `race` is on and a lap could be found. */ race?: ResolvedRace; } /** * One solid per wall and roof piece of every lot, grounded the way the city renderer grounds it: * grade at the highest lot corner, foundations down to half a metre under the lowest. * @capability city-district editor-authorable procedural city district (streets, zoning, buildings, furniture) */ export declare function citySolids(city: ResolvedCity, sampleHeight?: (x: number, z: number) => number): WorldSolid[]; /** Extended resolve context: sibling `cityzone` volumes that override the band/mix locally. */ export interface CityResolveContext extends SceneKindResolveContext { zoneOverrides?: readonly SceneKindObject[]; } /** * Synthesize the deterministic city plan for one `city` volume: streets → bridges → parks → zoned * frontage lots with massing pieces → furniture, all in the volume's local frame and then * rotated/translated into world space. Same volume (id, footprint, meta) over the same terrain * always resolves to the identical plan. When `context` provides a ground sampler, lots respect * the `maxSlope` cliff rule — hillside and canyon districts keep their steep faces open. When * `context.zoneOverrides` carries sibling `cityzone` volumes, lots inside them adopt the override * band/mix. Returns null without a usable footprint. * * @capability city-district resolve a `city` volume into deterministic streets, zoned lots, parks, and furniture */ export declare function resolveCityObject(object: SceneKindObject, context?: CityResolveContext): ResolvedCity | null; /** Registers the `city` + `cityzone` scene kinds (schema + resolver). Called by {@link registerBuiltinSceneKinds}. @internal */ export declare function registerCityKind(): void;