/** * Per-corridor-edge physical load of a train: tare + length of the wagons * spanning each edge, plus the cargo weight riding it. Used to validate that * a planned mid-route COUPLE keeps every leg within the locomotives' pull * weight and train length limits — a wagon cut at Mojo frees its tare/length * on the edges past Mojo, a wagon coupled there adds its own only from there. */ export interface EdgeLoad { weightTons: number; lengthMeters: number; } export interface EdgeWagonSpan { /** Half-open edge span [fromEdge, toEdge) the wagon physically rides. */ fromEdge: number; toEdge: number; tareTons: number; lengthMeters: number; } export interface EdgeCargoLeg { fromEdge: number; toEdge: number; weightTons: number; } export function computeEdgeLoads( edgeCount: number, wagonSpans: readonly EdgeWagonSpan[], cargoLegs: readonly EdgeCargoLeg[], ): EdgeLoad[] { const loads: EdgeLoad[] = Array.from({ length: Math.max(1, edgeCount) }, () => ({ weightTons: 0, lengthMeters: 0, })); const clamp = (edge: number) => Math.min(Math.max(edge, 0), loads.length); for (const span of wagonSpans) { for (let e = clamp(span.fromEdge); e < clamp(span.toEdge); e += 1) { loads[e].weightTons += span.tareTons; loads[e].lengthMeters += span.lengthMeters; } } for (const cargo of cargoLegs) { for (let e = clamp(cargo.fromEdge); e < clamp(cargo.toEdge); e += 1) { loads[e].weightTons += cargo.weightTons; } } return loads; }