/** Deterministic, renderer-independent authored detail for freeway corridors. */ import type { TransportPack } from "./types.ts"; import { buildRoutePath, sampleRoute } from "./vehicleSim.ts"; export type FreewayIdentity = "us-highway" | "interstate"; export type RoadsideFeatureKind = "oak" | "orchard" | "power-pole" | "silo" | "route-sign"; export interface FreewayWorldSample { distanceM: number; lat: number; lng: number; headingDeg: number; elevationPhase: number; lanesPerDirection: number; } export interface FreewayRoadsideFeature { id: string; kind: RoadsideFeatureKind; distanceM: number; side: -1 | 1; setbackM: number; scale: number; } export interface FreewayRoutePlan { routeId: string; identity: FreewayIdentity; shield: "101" | "5"; character: "coastal-oak" | "central-valley"; samples: readonly FreewayWorldSample[]; roadside: readonly FreewayRoadsideFeature[]; } export interface FreewayWorldPlan { seed: number; routes: readonly FreewayRoutePlan[]; } function hash(value: string): number { let out = 2_166_136_261; for (let index = 0; index < value.length; index += 1) { out ^= value.charCodeAt(index); out = Math.imul(out, 16_777_619); } return out >>> 0; } function seeded(seed: number): () => number { let state = seed >>> 0; return () => { state += 0x6d2b79f5; let value = state; value = Math.imul(value ^ (value >>> 15), value | 1); value ^= value + Math.imul(value ^ (value >>> 7), value | 61); return ((value ^ (value >>> 14)) >>> 0) / 4_294_967_296; }; } /** * Compile visual-world intent without Three.js or wall-clock state. The plan is * suitable for snapshots, replay provenance, server-side validation, and a * browser renderer. It intentionally derives only from the open transport pack. */ export function buildFreewayWorldPlan( pack: TransportPack, options: { seed?: number; sampleCount?: number } = {}, ): FreewayWorldPlan { const seed = options.seed ?? 101_005; const sampleCount = Math.max(12, Math.min(128, Math.floor(options.sampleCount ?? 56))); const segments = new Map(pack.segments.map((segment) => [segment.id, segment])); const routes = pack.routes.map((route): FreewayRoutePlan => { const path = buildRoutePath(pack, route.id); const first = segments.get(route.segmentIds[0] ?? ""); const identity: FreewayIdentity = first?.kind === "us-highway" ? "us-highway" : "interstate"; const rand = seeded(seed ^ hash(route.id)); const samples = Array.from({ length: sampleCount }, (_, index): FreewayWorldSample => { const distanceM = (index / (sampleCount - 1)) * path.lengthM; const sample = sampleRoute(path, distanceM); const segment = segments.get(sample.segmentId); return { distanceM, lat: sample.lat, lng: sample.lng, headingDeg: sample.headingDeg, // A reproducible visual grade cue. Actual ground height is supplied by // the renderer; this phase only varies berm/vegetation rhythm. elevationPhase: Math.sin(index * 0.43 + rand() * 0.35), lanesPerDirection: Math.max(2, Math.min(5, segment?.lanesPerDirection ?? 2)), }; }); const featureCount = identity === "us-highway" ? 96 : 88; const roadside = Array.from({ length: featureCount }, (_, index): FreewayRoadsideFeature => { const sign = index % 23 === 2; const power = identity === "interstate" && index % 4 === 0; const silo = identity === "interstate" && index % 13 === 7; const kind: RoadsideFeatureKind = sign ? "route-sign" : silo ? "silo" : power ? "power-pole" : identity === "us-highway" && index % 3 !== 1 ? "oak" : "orchard"; return { id: `${route.id}:${String(index).padStart(2, "0")}`, kind, distanceM: ((index + 0.7 + rand() * 0.45) / featureCount) * path.lengthM, side: rand() < 0.5 ? -1 : 1, setbackM: kind === "route-sign" ? 13 : 18 + rand() * 38, scale: 0.78 + rand() * 0.58, }; }); return { routeId: route.id, identity, shield: identity === "us-highway" ? "101" : "5", character: identity === "us-highway" ? "coastal-oak" : "central-valley", samples, roadside, }; }); return { seed, routes }; }