import { TrainBend, TrainBranch } from '../game/TrainData'; import { TrainLayoutEntry, TrainLayoutStyle } from './trainLayout'; export type TurnSide = 'left' | 'right'; /** Default pivot magnitude. The interactive UI only produces square corners. */ export declare const TURN_DEGREES = 90; /** * Signed turn (degrees) for a side. Headings use the screen convention * (0° = +x, +90° = +y / downward), so a `+90` turn rotates +x toward +y, which * reads as a clockwise/"right" turn on screen. */ export declare function sideToTurn(side: TurnSide, degrees?: number): number; export declare function oppositeSide(side: TurnSide): TurnSide; /** * Default turn side in offset mode: fold toward the empty side — the one * opposite the lane the zigzag biases into (`outwardSign`). A `+90` turn heads * toward the heading's `+perp`; outwardSign is measured on that same perp axis, * so the empty side is `-outwardSign`, i.e. side = outwardSign >= 0 ? 'left' : 'right'. */ export declare function offsetDefaultSide(angle: number, outwardSign?: number): TurnSide; export interface TableBounds { width: number; height: number; } /** * Default turn side in linear mode: fold toward whichever perpendicular side has * more open table from the bend point. Distance is measured from `point` along * each perpendicular until it exits the table rectangle; the roomier side wins. * Ties (e.g. dead-center) fall back to 'right'. */ export declare function linearDefaultSide(point: { x: number; y: number; }, angle: number, bounds: TableBounds): TurnSide; export interface BuildTrainTilesInput { startX: number; startY: number; angle: number; layoutStyle: TrainLayoutStyle; } /** Flattens a branch (with feet and bends) to its world-space tiles. */ export declare function buildBranchTiles(branch: TrainBranch, input: BuildTrainTilesInput): TrainLayoutEntry[]; /** Replaces (or removes) the bend at `index`, returning a new bends array. */ export declare function withBendAt(bends: readonly TrainBend[] | undefined, index: number, turn: number | null): TrainBend[]; export interface ResolveBendResult { /** The legal turn to apply, or null when no side is collision-free. */ turn: number | null; /** Why null: 'blocked' = both sides collide; never set on success. */ reason?: 'blocked'; } export interface ResolveBendInput { branch: TrainBranch; index: number; build: BuildTrainTilesInput; /** Tiles belonging to every OTHER path; a bend may not intersect these. */ obstacles: readonly TrainLayoutEntry[]; /** Preferred side to try first (from the mode's heuristic). */ preferredSide: TurnSide; /** Turn magnitude in degrees (default 90). */ degrees?: number; } /** * Picks a collision-free turn for a new bend at `index`. Tries the preferred * side first, then the opposite; a candidate is rejected if the resulting path * crosses itself or any obstacle path. Returns `{ turn: null, reason: 'blocked' }` * when neither side is legal, so the caller can refuse the bend. */ export declare function resolveBend({ branch, index, build, obstacles, preferredSide, degrees, }: ResolveBendInput): ResolveBendResult; /** * Cycles a tile's bend on repeated clicks: none → preferred legal side → * opposite legal side → none. Skips sides that collide. Returns the next bends * array, or the unchanged input when no legal bend exists. */ export declare function cycleBendAt(branch: TrainBranch, index: number, build: BuildTrainTilesInput, obstacles: readonly TrainLayoutEntry[], preferredSide: TurnSide, degrees?: number): { bends: TrainBend[]; changed: boolean; blocked: boolean; };