The pack
roadData
function roadData(): Pack<{ roadData: typeof api }>
Install the road data API.
Helpers
expandRoad
function expandRoad(doc: RoadDocument): RoadDocument
Bake generated bodies into plain pieces and surfaces (visit 0), for a tool to edit.
formatPieces
function formatPieces(pieces: readonly PieceDef[]): string | null
Write pieces back as notation, or null when some piece uses something the notation cannot say (enter/leave, ease, shoulder, a curvature that is not a whole-ish radius, or width with other changes on one piece).
fromRoutePieces
function fromRoutePieces(pieces: readonly RoutePiece[], o: FromRouteOptions = {}): RoadDocument
Convert route's inline RoadPiece list into a road document (one section; a levelled loop like route's).
loadRoad
function loadRoad(input: string | unknown): RoadData
Load a road from JSON text or a parsed object: validates (throwing RoadDataError listing every issue) and indexes it.
parsePieces
function parsePieces(src: string): PieceDef[]
Parse the compact notation. Throws a NotationError naming the token and the expected shape.
parseRoad
function parseRoad(input: string | unknown): { doc: RoadDocument; warnings: Issue[] }
Parse JSON text (or accept an object) and validate it; returns the document and its warnings, or throws RoadDataError.
planLayout
function planLayout(road: RoadData, o: PlanOptions = {}): PlanLayout
Lay the whole graph out flat.
ribbonCrossings
function ribbonCrossings(leg: RibbonLeg, o: { minApart?: number } = {}): RibbonCrossing[]
Every place a ribbon crosses itself in plan (reference lines, stretches at least minApart metres apart along the
course), with the height between the two roads: an authoring check that a bridge clears the road beneath it.
ribbonIndex
function ribbonIndex(leg: RibbonLeg, cell = 24): RibbonIndex
Index a ribbon's vertices on a plan grid. A track that crosses itself, or loops back beside itself, needs it: scenery placed beside one stretch can land on another, so a consumer drops ground objects whose footprint meets any deck. Distances are to the vertices' cross-sections (accurate to half a step along the road).
ribbonLeg
function ribbonLeg(path: RoadPath, o: RibbonLegOptions): RibbonLeg
Lay a stretch of a path out in world space (see the module comment for the frame).
ROAD_SCHEMA
const ROAD_SCHEMA: Schema
The road file's JSON Schema (draft 2020-12).
routeLeg
function routeLeg(path: RoadPath, o: RouteLegOptions): RouteLeg
Lay a stretch of a path into route's PseudoRoad options. Sample the path's strips per segment for split roads.
SAMPLE_ROADS
const SAMPLE_ROADS: Record<string, RoadDocument>
The road files of this folder, by name.
saveRoad
function saveRoad(doc: RoadDocument): string
Canonical JSON text of a road document (ends with a newline). Does not validate: call validateRoad first if unsure.
topdownSource
function topdownSource(path: RoadPath, o: TopdownSourceOptions = {}): TopdownSource
Draw a path with road-view-topdown (use frame: "straight" on the path for a vertical scroller).
validateRoad
function validateRoad(doc: unknown): ValidationResult
Validate a parsed road document (any value). Never throws.
Functions
forkShift
function forkShift(e: { dx: number; z: number; bend: number; diverge: number }, z: number): number
How far a thing fed before a fork was decided moves sideways when it is (curved frame): x += forkShift(e, z)
for scenery, traffic or anything placed in the previous frame at path distance z. Before the fork end it is the
rebase dx; along the chosen connector it also takes out the connector's own bend.
Classes
NotationError
class NotationError extends Error
A notation problem: which token and why.
readonly token: number— Zero-based token index.constructor(message: string, token: number)
RoadData
class RoadData
A loaded, validated road.
readonly doc: RoadDocument— The document as loaded (edit a copy andloadRoadit again to change the road).readonly warnings: Issue[]— Warnings from validation (width jumps, loops that do not close in height).constructor(doc: RoadDocument, warnings: Issue[] = [])get ids(): string[]— Section ids in document order.def(id: string): SectionDef— A section's definition. Throws on an unknown id.has(id: string): boolean— True when the section exists.successors(id: string): LinkDef[]— Successor links, left to right.predecessors(id: string): string[]— Predecessor ids, in document order.compiled(id: string, o: CompileOptions = {}): CompiledSection— A compiled section (visit 0, forward, unscaled unless given).length(id: string, visit = 0): number— Length of a section (for a generated body that varies by visit, pass the visit).region(id: string, s: number): ResolvedRegion— The resolved region at section-local s (visit 0, forward).path(o: PathOptions = {}): RoadPath— A path: sections laid end to end as driven.
RoadDataError
class RoadDataError extends Error
Thrown by loadRoad (and parseRoad) with every issue found.
readonly issues: Issue[]— Every problem, as JSON pointers and messages.constructor(issues: Issue[], what = "road file")
RoadPath
class RoadPath
A road as driven.
readonly road: RoadData— The loaded road this path drives.readonly frame: "curved" | "straight"—curvedorstraightframe, fromPathOptions.frame.readonly reverse: boolean— Whether the path drives backwards through predecessors.readonly conditions: ReadonlySet<string>— The conditions switched on, fromPathOptions.conditions.readonly avoidDeadEnds: boolean— Whether forks avoid branches that are dead ends.readonly layer: Layerconstructor(road: RoadData, o: PathOptions = {})get revision(): number— Bumped whenever road already laid changes (a fork decided, a restore).get start(): number— Path distance where the laid road starts.get end(): number— End of the road laid so far, previews included.get known(): number— End of the decided road (previews past an undecided fork excluded), laid up to 4 km past the lastadvance.get sceneryKnown(): number— End of the road whose scenery is final:knownplus, past an undecided fork, every branch's connector (its scenery is previewed in the current frame; applyforkShiftto it when the fork is decided). Feed scenery only up to here:field.feed(path.sceneryStream(), Math.min(ahead, path.sceneryKnown)), so no stretch is skipped.get instances(): readonly Inst[]— The laid instances (read-only view, for tools and diagnostics).static readonly KNOWN_AHEAD: 4000— How far past the lastadvancetheknowngetters lay road (a draw distance with room to spare).ensure(z: number): void— Lay road until it reaches z (or the road ends, or an undecided fork blocks).locateSection(z: number): { section: string; kind: Inst["kind"]; visit: number; s: number; z0: number; length: number } | null— Where z is: the section (or connector), its visit and the local distance. Null beyond the laid road.sample(z: number): RoadSample | null— Sample the road at path distance z; null beyond the laid road (the end, or past an undecided preview).spansAt(z: number): Span[]— The whole cross-section at z as spans in paint order (later spans paint over earlier): a top-down source row.surfaceAt(z: number, x: number): string— The surface under (z, x): the last span covering it.laneAt(z: number, x: number): LaneInfo | null— Lane, strip and surface under (z, x).featuresAt(z: number, x?: number): PathFeature[]— Features covering z (ramps over their length), optionally only those whose lateral range holds x.featuresIn(z0: number, z1: number): PathFeature[]— Features starting or lying in (z0, z1], in z order.markersIn(z0: number, z1: number): PathMarker[]— Markers whose point or range meets (z0, z1], in z order.env(z: number): RoadEnvOut— The vehicle-handling road environment at distance z: the whole cross-section (edges, surface bands, walls, ramps, tunnel, drops), which the vehicle measures its own lateral position against. Angles in radians; walls, ramps and tunnel only where present. For what lies under one lateral position, uselaneAt,surfaceAtorfeaturesAt.choose(branch: number): string | null— Decide the next undecided fork ahead of the lastadvance(AI, scripts). Returns the fork section, or null.commitBranch(forkSection: string, x: number, visitZ?: number): number— The branch a lateral x at a fork's end commits to (nearest strip; dead ends avoided when asked).advance(z: number, x: number): PathEvent[]— Advance the vehicle to z at lateral x (in the path's frame, before any rebase this call reports). Lays road, decides forks it reaches, and returns what happened since the last call, in z order: sections entered, fork decisions (curved frame: applydxto the vehicle,vehicle.rebase({ dx })), merges, and markers entered, left and passed.trim(z: number, keep = 200): void— Forget instances that end before z - keep (endless roads). The current one is always kept.sceneryIn(z0: number, z1: number): ScenePlacement[]— Placements from region pools and landmarks with z in (z0, z1], sorted by z then id (decided road only).sceneryStream(name = "road-scenery"): { name: string; options: { seed: number; name: string; sections: never[] }; between(z0: number, z1: number): ScenePlacement[] }— A scaled-depth compatible stream:field.feed(path.sceneryStream(), Math.min(ahead, path.sceneryKnown)). Feed only up tosceneryKnown(the field's cursor moves to whatever you pass, so a stretch fed before it existed is lost).snapshot(): PathSnapshot— Plain-JSON state.restore(s: PathSnapshot): void— Restore a snapshot taken from a path of the same road and options.
Constants
ROAD_FORMAT
const ROAD_FORMAT: "road-data"
The format tag every road file carries.
ROAD_VERSION
const ROAD_VERSION: 1
The current format version this pack reads and writes.
Types
CrossSection
interface CrossSection
The cross-section at one distance, in the path's frame.
centre: number— Carriageway centre of the driven road.halfWidth: number— Half the tarmac width of the driven road, metres, median excluded.median: number— Full width of the central island of the driven road, metres; 0 for none.shoulder: number— Width of the shoulder band outside each tarmac edge, metres.lanes: number— Lanes across the whole tarmac of the driven road.strips: Strip[]— Every strip drawn here (own and foreign).left: number— Outermost tarmac edges of the own strips.right: number— Lateral position of the right outer tarmac edge of the own strips, metres, positive to the driver's right.medians: number[]— Centres of medians and of gaps between neighbouring own strips.mirror: boolean— Laterals are mirrored (reverse driving).
CrossSectionDef
interface CrossSectionDef
Values of the cross-section: half-width, lanes, median and shoulder.
halfWidth?: number— Half the tarmac width, median excluded: outer edges at centre +- (halfWidth + median / 2). Default 6.lanes?: number— Lanes across the whole tarmac, left to right (a median gives the left carriageway floor(n / 2)). Default 2.median?: number— Full width of a central island (0 = none). Default 0.shoulder?: number— Width of the shoulder band outside each tarmac edge. Default 0.
DataBag
type DataBag = Record<string, unknown>
Free-form data carried through load and save untouched. The format never reads it.
DefaultsDef
interface DefaultsDef extends CrossSectionDef
Document defaults: the cross-section a section starts with when it gives none, plus default region and surfaces.
region?: string— Region used where a section names none.surface?: string— Carriageway surface where neither section nor region gives one. Default "tarmac".off?: string— Off-road surface where neither section nor region gives one. Default "grass".ground?: number— Height of a flat ground plane under the road, metres in the path's height frame (the path starts at 0). Set it and the road is a deck standing above the ground wherever it is higher (an elevated circuit on supports): samples reportgroundandelevation, kinds withon: "ground"stand on it, and the vehicle environment reports the drop beyond every edge without a barrier. Omitted: the ground follows the road (every road before this).
EntryDef
interface EntryDef
A named start: Night Driver's reverse set, a second course in the same file.
section: string— Section to start in.s?: number— Section-local distance to start at. Default 0 (or the section end when reversed).reverse?: boolean— Drive the road backwards from here. Default false.
FeatureDef
interface FeatureDef
A terrain feature: ramp, jump, wall, tunnel, bridge, or any free type.
anchor?: "start" | "end"— "start" (default): positions are metres from the section start. "end": metres BEFORE the section end (for generated bodies whose length varies by visit); a range may then be written either way round.id: string— Stable id.type: string— Known: ramp, jump, wall, tunnel, bridge. Any other string passes through.at?: number— Point position (ramps, boathouses).from?: number— Range start.to?: number— Range end.lateral?: LateralRange— Across.when?: string[]— Only when the path's conditions include one of these.data?: DataBag— Free data.length?: number— Ramp: length along the road, metres. Default 4.angle?: number— Ramp launch angle / wall slope from horizontal, DEGREES.height?: number— Ramp lip height / wall height, metres.side?: "left" | "right" | "both"— Wall: which side.ridable?: boolean— Wall: can be driven up. Default angle < 69 degrees.light?: number— Tunnel: light level 0..1. Default 0.4.ceiling?: number— Tunnel: clearance above the road, metres.walls?: boolean— Tunnel: solid side walls. Default true.rails?: boolean— Bridge: rails along the deck. Default true.gaps?: [number, number][]— Bridge: missing deck spans [from, to] (bridge out).
ForkDef
interface ForkDef
Out Run's road split at the end of a section with two or more successors.
widen: number— The lastwidenmetres of the section, over which the strips move apart.median: number— Gap between neighbouring strips at the end of the widen.diverge?: number— Connector road after the decision on each branch, metres. Default 0.curve?: number— Curvature with which an outer branch bends away duringdiverge(1/m). Default 0.trail?: number— Metres after the connector for which the untaken strips are still reported. Default = diverge.medianSurface?: string— Surface of the gap between strips. Default the region's medianSurface.scenery?: PoolRule[]— Scenery along the connectors (both sides and the median between the diverging roads). Default the parent region's pools.
FromRouteOptions
interface FromRouteOptions
Options for fromRoutePieces.
segmentLength?: number— route segment length (world units). Default 200 (route's default).unitsPerMetre?: number— World units per metre. Default 1.roadWidth?: number— route roadWidth (world units, a half-width). Default 1000.lanes?: number— Lanes. Default 3.closeLoop?: boolean— route closeLoop: the road is a levelled loop (default true, as in route).id?: string— Document and section id. Default "route".
GenerateDef
interface GenerateDef
A seeded section body: generated pieces and patches.
length: number | [number, number]— Body length, metres, or a [min, max] range.pieceLength?: [number, number]— Piece length range. Default [90, 220].weights?: { straight?: number; curve?: number; hill?: number; shift?: number; width?: number; split?: number }— Relative weights of piece kinds. Default straight 3 plus 1 for each kind whose range is given.curve?: [number, number]— Curvature magnitude range (1/m); sign random.climb?: [number, number]— Climb magnitude range (m); sign random.shift?: number— Largest centre shift of one shift piece (m).halfWidth?: [number, number]— Half-width range for width pieces.median?: [number, number]— Island width range for split pieces (open, hold, close).patches?: { per1000: number; surface: string; width?: [number, number]; length?: [number, number] }— Surface patches scattered on straights.vary?: "fixed" | "visit"— "fixed" (default): one body for every visit; "visit": regenerated each time the path re-enters the section.seed?: number— Mixed with the document seed and the section id. Default 0.close?: { halfWidth?: number; median?: number; shoulder?: number; length?: number }— The closing piece: the body's lastlengthmetres (default min(100, pieceLength[0])) ease to these values, before a fork's widen. Omitted values ease to the successors' start values when they all agree; the median closes to 0.
Issue
interface Issue
One problem found in a document: a JSON pointer and a message.
path: string— JSON pointer into the document ("/sections/3/pieces/2/radius").message: string— What is wrong and, where possible, how to fix it.
KindDef
interface KindDef
A kind of roadside object (scenery pools and landmarks refer to it by id).
w: number— Drawn width (lateral extent), metres: what must stay off the road. For aspankind: extra width beyond each road edge.hit?: number— Collision width, metres (a palm's trunk under a 7 m canopy). Defaultw.h: number— Height, metres.d?: number— Depth along the road, metres. Default min(w, h).y?: number— Height of the anchor above the road surface. Default 0.solid?: boolean— Can be hit (the consumer decides what hitting means). Default false.span?: boolean— Spans the road (gateway arches, START / GOAL banners): centred across it, as wide as the road. Default false.variants?: number— Number of art variants: placements pick one deterministically. Default 1.anchor?: "base" | "centre"—base(default): y is the bottom;centre: y is the centre.on?: "road" | "ground"— What it stands on:road(default: y is above the road surface) orground: y is above the section'sgroundplane (a tree below an elevated deck). Without a ground plane both are the road.data?: DataBag— Free data copied onto every placement.
LaneInfo
interface LaneInfo
Where the vehicle is across the road.
strip: number— Index of the own strip it is over (left to right) or nearest.lane: number | null— Lane index on the driven road, or null when off the tarmac.onRoad: boolean— On tarmac.surface: string— Surface under it.
LateralPoint
type LateralPoint = { x: number } | { lane: number } | { side: "left" | "right" | "median"; distance?: number }
A lateral point: the centre when omitted.
LateralRange
type LateralRange = | { x: [number, number] } | { lanes: [number, number] } | { side: "left" | "right" | "both" | "median"; band: [number, number] }
A lateral range: across the whole tarmac when omitted.
LinkDef
interface LinkDef
A successor with extras (a medium change through a boathouse).
to: string— Successor section id.via?: string— How the link is made: required when the medium changes ("boathouse").data?: DataBag— Free data.
MarkerDef
interface MarkerDef
An event marker: data only, games hook into it.
anchor?: "start" | "end"— "start" (default): positions are metres from the section start. "end": metres BEFORE the section end (for generated bodies whose length varies by visit); a range may then be written either way round.id: string— Stable id (unique within its section).type: string— Free type: start, lap, checkpoint, goal, grid, sign, banner, traffic, encounter, van, boss, music, tunnel ...at?: number— A point: passed.from?: number— A range start: entered.to?: number— A range end: left.lateral?: LateralRange— Counts only while the vehicle is inside it.when?: string[]— Only when the path's conditions include one of these.data?: DataBag— Free data.
MergeDef
interface MergeDef
Roads converging into this section: the mirror of a fork.
from: string[]— The converging predecessors, LEFT to RIGHT.narrow: number— The firstnarrowmetres of the section, over which the strips close up.median: number— Gap between neighbouring strips at the start of the narrow.converge?: number— Connector road before the section on each incoming road, metres. Default 0.curve?: number— Curvature of an outer incoming road duringconverge(1/m). Default 0.medianSurface?: string— Surface of the gap between strips. Default the region's medianSurface.
ObjectDef
interface ObjectDef
A hand-placed landmark object.
anchor?: "start" | "end"— "start" (default): positions are metres from the section start. "end": metres BEFORE the section end (for generated bodies whose length varies by visit); a range may then be written either way round.id: string— Stable id.kind: string— Kind id.at: number— Section-local position.lateral?: LateralPoint— Across: the centre when omitted.variant?: number— Art variant. Default 0.when?: string[]— Only when the path's conditions include one of these.data?: DataBag— Free data.
PathEvent
type PathEvent = | { type: "section"; section: string; visit: number; z: number; from: string | null; via: string | null } | { type: "fork"; section: string; branch: number; to: string; dx: number; deadEnd: boolean; z: number; bend: number; diverge: number } | { type: "merge"; section: string; from: string | null; branch: number; dx: number; z: number } | { type: "enter" | "leave" | "pass"; marker: MarkerDef; section: string; visit: number; z: number; lane: number | null; strip: number | null } | { type: "end"; section: string; z: number; deadEnd: boolean }
What advance reports.
PathFeature
interface PathFeature
A feature located on the path.
feature: FeatureDef— The feature's definition from the road file.section: string— Id of the section that holds it.visit: number— Visit number of that section on this path (0 = first time).z0: number— Path distance where it starts and ends (equal for a point).z1: number— Path distance where it ends; equal toz0for a point.lateral: [number, number][]— Lateral intervals at its start.
PathMarker
interface PathMarker
A marker located on the path.
marker: MarkerDef— The marker's definition from the road file.section: string— Id of the section that holds it.visit: number— Visit number of that section on this path (0 = first time).z0: number— Path distance of the point or range start.z1: number— Range end (= z0 for a point).lateral: [number, number][] | null— Lateral intervals, null when the marker spans the whole width.
PathOptions
interface PathOptions
Options for road.path().
start?: string— Section to start in. Default the document'sstart(or the entry's section).entry?: string— A named entry from the document'sentries(section, s and reverse).s?: number— Section-local distance to start at (in the driven direction). Default 0.frame?: "curved" | "straight"—curved(pseudo-3D, default) orstraight(a top-down scroller: curvature drifts the road sideways).reverse?: boolean— Drive backwards through predecessors.conditions?: string[]— Conditions that switch on surfaces, features, markers, objects and pool rules withwhen.avoidDeadEnds?: boolean— At a fork, never commit to a branch whose section is a dead end (Spy Hunter's bridge out). Default false.lookahead?: number— Length of the straight preview laid past an undecided fork when the fork has no connector. Default 300.
PathSnapshot
interface PathSnapshot
Plain-JSON state of a path.
insts: Inst[]— The laid stretches of road, in order: sections, fork and merge connectors and undecided previews.visits: Record<string, number>— How many times each section has been entered, by section id.uid: number— The id the next laid stretch will take.lastZ: number | null— Path distance of the lastadvancecall, or null before the first.active: string[]— Keys of the range markers the vehicle is inside, as<stretch id>:<marker id>.revision: number— Counter of changes to road already laid; seeRoadPath.revision.ended: boolean— Whether theendevent has been reported.
PieceDef
interface PieceDef
One stretch of road.
length: number— Length, metres (> 0).curve?: number— Curvature (1/m, + right). Not withradius.radius?: number— Signed radius (m, + right-hand bend). Not withcurve.enter?: number— Metres over which curvature and bank ease in from 0. Default 20% of the length (0 with ease false).leave?: number— Metres over which curvature and bank ease out to 0. Default 20% of the length (0 with ease false).climb?: number— Metres climbed over the piece (negative descends).bank?: number— Bank in DEGREES, positive raises the right side; held with the curvature envelope.shift?: number— Lateral move of the carriageway centre over the piece, metres (+ right).halfWidth?: number— Half-width reached at the piece end.median?: number— Median width reached at the piece end.shoulder?: number— Shoulder width reached at the piece end.lanes?: number— Lane count from the piece start on.ease?: boolean— Smoothstep transitions (true, default) or linear ones.name?: string— A name for tools and diagnostics.
PlanLayout
interface PlanLayout
The laid map.
sections: PlanSection[]— Every stretch laid, breadth first from the start; a fork lays all its branches.gaps: { from: string; to: string; a: { x: number; y: number }; b: { x: number; y: number } }[]— Links whose ends do not meet in plan (a graph merge or a loop that does not close).bounds: { minX: number; minY: number; maxX: number; maxY: number }— Plan extent of every strip edge, metres.
PlanOptions
interface PlanOptions
Options for planLayout.
step?: number— Row spacing, metres. Default 10.conditions?: string[]— Conditions forwhenfilters.start?: string— Start section. Default the document's start.
PlanPoint
interface PlanPoint
A located point on the map.
x: number— Plan x, metres.y: number— Plan y, metres; y grows down the map.s: number— Section-local distance, metres.
PlanRow
interface PlanRow
One sampled row of a laid section.
s: number— Section-local distance.x: number— Plan position and heading of the reference line (y down, heading 0 up).y: number— Plan y of the reference line, metres; y grows down the map.heading: number— Plan heading of the reference line, radians, clockwise from up the map.strips: { edges: [number, number]; own: boolean }[]— Lateral [left, right] tarmac edges of every strip, with whether it is part of this road.shoulder: number— Shoulder width.region: string— Region and look.look: string | null— Look key of the region, or null when it names none.height: number— Road height, metres.
PlanSection
interface PlanSection
One laid section of the map.
id: string— Id of the laid stretch: a section's id, or a connector's (fork:a:1,merge:b:0,undecided:a).kind: Inst["kind"]— What the stretch is: asection, aforkconnector, amergeconnector or anundecidedpreview.section: string— Id of the real section it belongs to.length: number— Length, metres.rows: PlanRow[]— Rows sampled along the stretch, about one everystepmetres, including both ends.markers: (PlanPoint & { marker: MarkerDef; end: PlanPoint | null })[]— The section's markers located in plan;endis where a range marker stops, else null.features: (PlanPoint & { feature: FeatureDef; end: PlanPoint | null })[]— The section's features located in plan;endis where a ranged feature stops, else null.objects: (PlanPoint & { object: ObjectDef })[]— The section's landmark objects located in plan.
PoolRule
interface PoolRule
One scenery rule of a region's pool.
kinds: (string | WeightedKind)[]— Kinds to choose from (plain ids weigh 1).side: "left" | "right" | "both" | "median" | "span"— Where: either side, one side, the median (or a fork's gap), or across the road.band?: [number, number]— Gap [near, far] from the tarmac edge to the object's NEAR side, metres (median: from the median centre). The drawn width is added, so a wide canopy never overhangs the road. Default [2, 10].spacing: number— Metres between slots.jitter?: number— Random shift of a slot as a fraction of the spacing, 0..0.49. Default 0.chance?: number— Chance a slot places anything, 0..1. Default 1.count?: number | [number, number]— Items per slot per side: a number or an inclusive [min, max]. Default 1.mirror?: boolean— With side "both": place each item at the same distance on both sides (avenues). Default false.minGap?: number— Smallest lateral gap between items of one slot and side. Default 0.from?: number— Only from this distance into the region span.to?: number— Only up to this distance into the region span.when?: string[]— Only when the path's conditions include one of these.
ProgressionKey
interface ProgressionKey
A geometry scaling key over distance driven.
at: number— Path distance, metres.curve?: number— Multiplier on curvature. Default 1.halfWidth?: number— Multiplier on half-width. Default 1.climb?: number— Multiplier on climb. Default 1.
RegionDef
interface RegionDef
A region: what a stretch of road looks like and what grows beside it.
name?: string— Display name.look?: string— Renderer key: palette, sky, backdrop layers.medium?: string— Medium of the carriageway ("road", "river", ...). Default "road".surface?: string— Carriageway surface. Default the document's default, else "tarmac" ("water" for medium river).off?: string— Ground beyond the shoulders. Default the document's default, else "grass".shoulderSurface?: string— Shoulder surface. Default "kerb".medianSurface?: string— Median and fork-gap surface. Default the region'soff.light?: string— Lighting cue: "day", "night" or any key.scenery?: PoolRule[]— The scenery pool: rules applied along every stretch of this region.data?: DataBag— Free data.
RegionSpanDef
interface RegionSpanDef
A region span within a section.
region: string— Region id.from?: number— Start, section-local metres. Default 0.to?: number— End. Default the section end.blend?: number— Metres over which renderers blend from the previous region. Default 0.
RibbonCrossing
interface RibbonCrossing
Where a ribbon passes over itself: two stretches crossing in plan, and the height between them.
lower: number— Course distances of the lower and the upper road at the crossing.upper: number— Course distance of the upper road at the crossing.x: number— Plan position (world x, z).z: number— Plan position along world z of the crossing (the plan's y).clearance: number— Height of the upper road above the lower one, metres.
RibbonFrame
interface RibbonFrame
A point and orientation on the ribbon (world units, unit vectors).
x: number— Reference-line position.y: number— Reference-line height, metres (world y).z: number— Reference-line position along world z, metres (the plan's y).fx: number— Forward (along the road, following its grade).fy: number— Forward vector, y component: the climb.fz: number— Forward vector, z component; heading 0 drives toward -z.rx: number— Right across the road (tilted by the bank).ry: number— Right vector, y component: the bank lifts the right side.rz: number— Right vector, z component.heading: number— Plan heading (radians, clockwise from up the map), bank and grade angle (radians, nose up positive).bank: number— Bank, radians; positive raises the right side.pitch: number— Grade angle, radians; nose up is positive.centre: number— Carriageway centre and half-width (metres from the reference line), shoulder width.halfWidth: number— Half the tarmac width, metres, median excluded.shoulder: number— Width of the shoulder band outside each tarmac edge, metres.ground: number— Height of the ground plane under the road (NaN when the ground follows the road).elevation: number— Road height above that ground (0 without a ground plane).
RibbonIndex
interface RibbonIndex
A spatial index over a ribbon's deck for "is this ground spot under or on the road anywhere?" queries.
nearest(x: number, z: number): { clear: number; k: number; height: number } | null— The nearest deck to a plan point (world x, z):clearis the plan distance from the point to the deck's outer edge (negative inside the deck's footprint),kthe nearest vertex andheightthe road height there.
RibbonLeg
interface RibbonLeg
A stretch of road laid in world space. Per-vertex arrays; vertex k is at course distance zBase + k * step.
zBase: number— Course distance of vertex 0, metres.step: number— Metres between vertices. For a closed leg, the lap length over the vertex count.count: number— Vertices. A closed leg's last segment runs from vertexcount - 1back to vertex 0.length: number— Course length covered (count * step when closed, (count - 1) * step otherwise).closed: boolean— Whether the leg is one whole lap of a closed circuit.x: Float64Array— Reference-line positions.y: Float64Array— Height of each vertex, metres (world y).z: Float64Array— World z of each vertex, metres (the plan's y).heading: Float64Array— Plan heading of each vertex, radians, clockwise from up the map.bank: Float64Array— Bank (radians, + raises the right side) and grade angle (radians).pitch: Float64Array— Grade angle of each vertex, radians; nose up is positive.centre: Float64Array— Carriageway centre at each vertex, metres from the reference line.halfWidth: Float64Array— Half the tarmac width at each vertex, metres.shoulder: Float64Array— Width of the shoulder band at each vertex, metres.ground: Float64Array— Ground plane height (NaN where the ground follows the road) and the road's height above it.elevation: Float64Array— Height of the road above the ground plane at each vertex, metres; 0 without a ground plane.barrierLeft: Float64Array— Height of a barrier (a wall that cannot be ridden) standing at the left / right edge, 0 for none.barrierRight: Float64Array— Height of a barrier at the right edge at each vertex, metres; 0 for none.regions: string[]— Region id and look key per vertex.looks: (string | null)[]— Look key per vertex, null where the region names none.locate(z: number): { k: number; t: number }— Vertex index at or before a course distance (wrapped when closed, clamped otherwise) and the fraction to the next.frame(z: number): RibbonFrame— The interpolated frame at a course distance.point(z: number, lateral: number, up?: number): { x: number; y: number; z: number }— World position of a pointlateralmetres right of the reference line andupmetres above the road surface.
RibbonLegOptions
interface RibbonLegOptions
Options for ribbonLeg.
from: number— Path distance of the first vertex.length: number— Length laid, metres (cut short where the laid road ends). For a closed lap: the lap length.step?: number— Metres between vertices. Default 4.closed?: boolean— The stretch is one whole lap of a closed circuit (aclosesection driven from its start): course distances wrap round it and the last vertex joins the first. Default false.
RoadDocument
interface RoadDocument
A road document: the whole .road.json file.
format: typeof ROAD_FORMAT— Always "road-data".version: typeof ROAD_VERSION— Format version.id: string— Stable id.name?: string— Display name.meta?: DataBag— Tool data, preserved and never read.seed?: number— Scenery and generation seed. Default 1.start: string— Section driven first.entries?: Record<string, EntryDef>— Named starts.defaults?: DefaultsDef— Defaults.kinds?: Record<string, KindDef>— Object kinds.regions?: Record<string, RegionDef>— Regions.progression?: ProgressionKey[]— Geometry scaling by distance driven.sections: SectionDef[]— The road graph.data?: DataBag— Free data.
RoadEnvOut
interface RoadEnvOut
The vehicle-handling RoadEnv (structural: no dependency).
curvature: number— Curvature, 1/m, positive bends right.grade: number— Rise over run.vertical: number— Change of grade per metre (negative over a crest).bank: number— Bank, radians, positive raises the right side.halfWidth: number— Half the tarmac width, metres.left: number— Lateral position of the left outermost tarmac edge of the road being driven, metres.right: number— Lateral position of the right outermost tarmac edge of the road being driven, metres.road: string— Carriageway surface id.off: string— Surface id beyond the road's edges.bands: Span[]— Surface spans across the road in paint order, later over earlier:spansAt(z)without its first, off-road span.walls?: { left?: { angle: number; height: number; ridable: boolean }; right?: { angle: number; height: number; ridable: boolean } }— Walls beside the road, per side: slopeanglein radians from horizontal,heightin metres andridable, whether a vehicle can drive up it. Present only where a wall feature stands.ramps?: { from: number; to: number; angle: number }[]— Ramps across the road: the lateral intervalfromtotoin metres and the launchanglein radians. Present only where a ramp feature is.tunnel?: boolean— True inside a tunnel feature; absent elsewhere.ceiling?: number— Clearance above the road in metres, where the tunnel gives one.drop?: { left?: number; right?: number }— The fall beyond each road edge to the ground plane (metres), where the road stands above it with no barrier on that side.
RoadSample
interface RoadSample
Everything known about the road at one distance. Laterals are metres in the path's frame.
z: number— Path distance.section: string— Section (or synthetic connector) id.s: number— Section-local distance.visit: number— Visit number of the section on this path (0 = first time).length: number— Length of that section.px: number— Plan position of the reference line (y down, heading 0 = up the map).py: number— Plan y of the reference line, metres; y grows down the map.heading: number— Plan heading, radians, positive clockwise.curvature: number— Curvature, 1/m, positive bends right.grade: number— Rise over run.vertical: number— Change of grade per metre (negative over a crest).height: number— Height, metres (accumulated along the path).bank: number— Bank, radians, positive raises the right side.ground: number | null— Height of the ground plane under the road (the section'sground), or null when the ground follows the road.elevation: number— How high the road stands above that ground (height - ground; 0 without a ground plane).centre: number— Carriageway centre.halfWidth: number— Half the tarmac width, metres, median excluded.median: number— Full width of the central island, metres; 0 for none.shoulder: number— Width of the shoulder band outside each tarmac edge, metres.lanes: number— Lanes, the previous lane count and the progress through the change (to fade lane lines).lanesFrom: number— The lane count before the latest change, to fade lane lines against.lanesT: number— Progress through that lane change, 0 to 1.left: number— Outermost tarmac edges of the road being driven.right: number— Lateral position of the right outermost tarmac edge of the road being driven, metres;leftis the other edge.strips: Strip[]— Every carriageway drawn here.zone: Zone | null— Fork/merge zone, if any.region: string— Region id, the region being blended from (or null) and the blend 0..1.regionFrom: string | null— Id of the region being blended from, or null when no blend is under way.regionT: number— The blend fromregionFromtoregion, 0 to 1.look: string | null— Renderer look key of the region, or null when it names none.medium: string— Medium of the carriageway here, such as "road" or "river": the section's override, else the region's; default "road".surface: string— Carriageway surface: the section's override, else the region's, else the document default, else "tarmac" ("water" on a river). Surface bands are not applied: seespansAt.off: string— Surface beyond the shoulders: the section's override, else the region's, else the document default, else "grass".shoulderSurface: string— Surface of the shoulder band: the region's, else "kerb".medianSurface: string— Surface of the median and of the gap between the strips of a fork: the region's, elseoff; inside a fork's zone the fork's ownmedianSurfacewins.light: string | null— The region's lighting cue, or null when it names none.
RouteLeg
interface RouteLeg
A stretch of a path as route's PseudoRoad options plus per-segment data.
options: { pieces: RoutePiece[]; segmentLength: number; roadWidth: number; lanes: number; closeLoop: false; camHeight?: number; drawDistance?: number; fov?: number; }—createRoad(leg.options): one exact piece per segment, no route easing, no loop levelling.zBase: number— Path distance of leg z = 0.segmentLength: number— Segment length in metres.unitsPerMetre: number— route world units per metre used for this leg.count: number— Segments.kappa: Float64Array— Per segment (metres, 1/m): curvature, half-width, carriageway centre, lanes, height at the segment start.halfWidth: Float64Array— Half-width of the road at the middle of each segment, metres.centre: Float64Array— Carriageway centre at the middle of each segment, metres, positive to the driver's right.lanes: Uint8Array— Lane count at the middle of each segment.height: Float64Array— Height at the start of each segment, metres.sections: string[]— Per segment: section (or connector) id, region id and look key.regions: string[]— Id of the region at the middle of each segment.looks: (string | null)[]— Look key of that region for each segment, or null when it names none.strips(seg: number): Strip[]— Strips drawn on a segment (metres, the path's curved frame).branches(seg: number): number[] | null— road-view-pseudo3dbranches: strip offsets in route world units, or null for one centred road.widthFactor(seg: number): number— Half-width of a segment over route's roadWidth (a presenter's width factor).segmentAt(z: number): number— Segment index of a leg z (metres fromzBase, clamped).
RouteLegOptions
interface RouteLegOptions
Options for routeLeg.
from: number— Path distance of the leg's z = 0.length: number— Leg length, metres (cut short where the laid road ends).segmentLength?: number— Segment length, metres. Default 5.unitsPerMetre?: number— route world units per metre. Default 1.roadWidth?: number— routeroadWidthin METRES (the half-width that x / roadWidth = 1 maps to). Default the widest half-width on the leg.camHeight?: number— Passed through to route: camera height (world units), draw distance (segments), field of view.drawDistance?: number— Passed through to route as its draw distance, in segments.fov?: number— Passed through to route as its field of view.
RoutePiece
interface RoutePiece
A route RoadPiece (structural).
length: number— Length of the piece in route world units.curve?: number— Curvature per segment: negative left, positive right. Default 0.hill?: number— Height climbed (+) or dropped (-) over the piece, in route world units. Default 0.name?: string— A name copied to each segment of the piece.ease?: boolean— Ease the curve and hill in and out instead of snapping. Default true.
ScenePlacement
interface ScenePlacement
One placed object. Shaped like scaled-depth's SceneryPlacement (sid, kind, x, y, z, w, h, d, anchor, data) plus road facts.
sid: string— Stable id:<section>~<visit>:<region>:<rule>:<slot>:<item>[L|R], or<section>~<visit>#<object id>for a landmark.kind: string— Id of the object's kind in the road file'skinds.section: string— Section (or connector) id and section-local distance.s: number— Section-local distance of the object, metres.z: number— Path distance.x: number— Lateral, metres in the path's frame (the object's centre).hit: number— Collision width (the kind'shit, default its drawn widthw).y: number— Anchor height above the road.w: number— Drawn width, metres: the kind'sw, or for aspankind the road's width plus twice that.h: number— Height, metres, from the kind.d: number— Depth along the road, metres: the kind'sd, else the smaller of its width and height.anchor: "base" | "centre"— Whetheryis the object's bottom (base) or its centre (centre), from the kind.solid: boolean— Whether the object can be hit; the consumer decides what that means. From the kind, default false.side: string— left, right, median, span, or landmark.variant: number— Art variant, a whole number below the kind'svariants, picked deterministically for pool scenery.landmark: boolean— True for a hand-placed landmark object, false for pool scenery.data: Record<string, unknown>— The kind'sdatamerged with the landmark's owndata.
Schema
interface Schema
The subset of JSON Schema this pack authors and interprets.
$schema?: string— The JSON Schema dialect the document follows, as a URI. The interpreter ignores it.$id?: string— The schema's own identifying URI. The interpreter ignores it.$ref?: string— A reference to a named definition in the root's$defs, written#/$defs/<name>. It stands in for that schema.$defs?: Record<string, Schema>— Named schemas that$refpoints to.title?: string— A short name for the schema. An annotation only.description?: string— What the value means, in a sentence. Error messages quote it, for a missing required property and for ananyOfthat matched no branch.type?: "object" | "array" | "string" | "number" | "integer" | "boolean" | "null" | ("object" | "array" | "string" | "number" | "integer" | "boolean" | "null")[]— The JSON type the value must have, or a list of types of which it must have one.integeris a whole number,numberany finite number.const?: string | number | boolean— The only value allowed.enum?: (string | number)[]— The values allowed, compared by equality.properties?: Record<string, Schema>— A schema for each named property of an object. A property it names is checked; others are subject toadditionalProperties.required?: string[]— Property names an object must have.additionalProperties?: boolean | Schema— What to do with properties not named inproperties:falserejects them as unknown; a schema checks their values.propertyNames?: Schema— A schema every property name of the object must satisfy.items?: Schema— The schema for each array item not covered byprefixItems.prefixItems?: Schema[]— Schemas for the first items of an array, by position; later items useitems.minItems?: number— Fewest items an array may have.maxItems?: number— Most items an array may have.minimum?: number— Smallest number allowed, inclusive.maximum?: number— Largest number allowed, inclusive.exclusiveMinimum?: number— A number must be greater than this (the bound itself is not allowed).pattern?: string— A regular expression a string must match, unanchored.anyOf?: Schema[]— Alternatives: the value must satisfy at least one. When none does, the error describes the branch the value most resembles.
SectionDef
interface SectionDef extends CrossSectionDef
A section: a node of the road graph with its own geometry.
id: string— Stable id.name?: string— Display name.region?: string— Region for the whole section.regions?: RegionSpanDef[]— Region spans; later spans win.surface?: string— Carriageway surface override.off?: string— Off-road surface override.medium?: string— Medium override.pieces?: PieceDef[] | string— The pieces: objects or the compact notation.generate?: GenerateDef— A seeded body appended after the pieces.level?: boolean— Subtract the section's net climb so it ends at its start height. Default false.close?: boolean— A closed circuit laid in 3D (a lap section that is its own successor): the plan is corrected so the section ends exactly where it starts, facing the same way, so every lap lies on the last and a track can cross over itself. The heading error is spread as a uniform curvature trim and the remaining gap linearly over the lap;validateRoadwarns when the authored turns miss a whole number of turns by more than 15 degrees or the gap is over 5% of the length. Use withlevelto close in height too. Default false.ground?: number— Height of the ground plane under this section (overridesdefaults.ground; see there).next?: (string | LinkDef)[]— Successors LEFT to RIGHT.end?: "goal" | "dead"— What an end (no successors) is. Default "goal".fork?: ForkDef— Split geometry (required with two or more successors).merge?: MergeDef— Converging geometry at the start.surfaces?: SurfaceBandDef[]— Surface patches.features?: FeatureDef[]— Terrain features.markers?: MarkerDef[]— Event markers.objects?: ObjectDef[]— Landmark objects.data?: DataBag— Free data.
Span
interface Span
One stretch of surface across the road.
from: number— Lateral start of the span, metres in the path's frame.to: number— Lateral end of the span, metres; the end itself is not covered.surface: string— Surface id across the span.
Strip
interface Strip
One carriageway strip, lateral metres in the path's frame.
offset: number— Centre of the strip.halfWidth: number— Half the tarmac width (median excluded).median: number— Central island width.lanes: number— Lanes.shoulder: number— Shoulder width outside each edge.branch: number | null— Branch index (left to right) during a fork or merge; null on a plain road.to: string | null— The section this strip leads to (forks), or comes from (merges, asfrom); null otherwise.medium: string— The medium of the road this strip belongs to.own: boolean— True when the strip is part of the road being driven here; false for an untaken branch or a preview.
SurfaceBandDef
interface SurfaceBandDef
A surface patch: oil, a puddle, gravel, a dirt shoulder, water.
anchor?: "start" | "end"— "start" (default): positions are metres from the section start. "end": metres BEFORE the section end (for generated bodies whose length varies by visit); a range may then be written either way round.id?: string— Optional stable id.surface: string— Surface id.from: number— Start, section-local metres.to: number— End.lateral?: LateralRange— Across: the whole tarmac when omitted.when?: string[]— Only when the path's conditions include one of these.data?: DataBag— Free data.
TopdownSource
interface TopdownSource
A road-view-topdown RoadSource (structural) drawing a path: v is path distance, u lateral metres.
spansAt(v: number): { from: number; to: number; surface: string }[]— The surface spans across the road at path distance v, in paint order (RoadPath.spansAt).featuresIn(v0: number, v1: number): { id: string; kind: string; u: number; v: number; width: number; length: number }[]— The features lying in (v0, v1] as drawable boxes: lateral centreuandwidth, path-distance centrevandlength, all metres.markingsIn(v0: number, v1: number): { id: string; u: number; from: number; to: number; width: number; dash?: number; gap?: number; colour?: string }[]— The lane-line dashes along [v0, v1): lateral positionu, path distancefromtoto,width, and optionaldash,gapandcolour, in metres.lookAt(v: number): string | null— The region look at v (a top-down renderer recolours per territory).readonly revision: number— Changes when road already laid changes (RoadPath.revision), telling a renderer to redraw.
TopdownSourceOptions
interface TopdownSourceOptions
Options for topdownSource.
laneLines?: boolean— Emit dashed lane lines as markings. Default true.dash?: number— Dash and gap, metres. Default 6 and 6.gap?: number— Gap between dashes, metres; default 6.lineWidth?: number— Line width, metres. Default 0.3.colour?: string— Line colour. Default white.
ValidationResult
interface ValidationResult
The result of validateRoad.
ok: boolean— No errors (warnings allowed).errors: Issue[]— Problems that stop the road loading.warnings: Issue[]— Suspicious but loadable (width jumps at junctions, a loop that does not close in height).
WeightedKind
interface WeightedKind
A weighted kind reference in a pool rule.
kind: string— Kind id.weight: number— Relative weight.
Zone
interface Zone
Where a sample is relative to a fork or merge.
kind: "widen" | "diverge" | "narrow" | "converge" | "undecided"— Which part of a fork or merge:widenthe strips moving apart at the end of a fork section,divergethe connector after the decision,narrowthe strips closing up at the start of a merge section,convergethe connector before it,undecidedthe straight preview past a fork not yet decided.u: number— Metres into the zone.length: number— Zone length.branch?: number— The branch taken (or arrived on), when known.branches: number— Number of strips.


