@inventure71/paddockjs 4.2.0 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/INSTALL_AND_UPDATE.md +23 -4
  3. package/README.md +31 -2
  4. package/docs/advanced_ai_pace.md +131 -0
  5. package/docs/advanced_physics_rebuild.md +166 -0
  6. package/docs/architecture.md +109 -30
  7. package/docs/cleanup_coverage.md +284 -0
  8. package/docs/component_inventory.md +31 -15
  9. package/docs/composable-layouts.md +2 -0
  10. package/docs/concepts.md +12 -1
  11. package/docs/custom_model_controller.md +1 -1
  12. package/docs/data_contract.md +33 -10
  13. package/docs/data_model.md +4 -2
  14. package/docs/efficiency_cleanup_2026-10-13.md +80 -0
  15. package/docs/grid_walk.md +80 -0
  16. package/docs/index.md +1 -0
  17. package/docs/keyboard_driving_fix.md +118 -0
  18. package/docs/limit_driving_2026-10-14.md +88 -0
  19. package/docs/maintainability_audit_2026-09-08.md +146 -0
  20. package/docs/racing_ai_improvements.md +145 -0
  21. package/docs/rules.md +43 -13
  22. package/docs/sense_contract.md +4 -1
  23. package/docs/system_specs.md +36 -11
  24. package/docs/testing-reliability-guide.md +5 -0
  25. package/docs/v11_improvements.md +108 -0
  26. package/package.json +23 -13
  27. package/src/api/MountedSimulatorComponents.js +101 -0
  28. package/src/api/PaddockSimulatorController.js +56 -81
  29. package/src/app/BrowserExpertAdapter.js +0 -6
  30. package/src/app/BrowserExpertExternalRenderer.js +72 -50
  31. package/src/app/ExternalTrackSnapshot.js +50 -0
  32. package/src/app/F1SimulatorApp.js +242 -297
  33. package/src/app/HostLifecycleEmitter.js +93 -0
  34. package/src/app/banners/raceDataBannerController.js +9 -4
  35. package/src/app/camera/cameraConstants.js +1 -1
  36. package/src/app/camera/cameraController.js +10 -13
  37. package/src/app/camera/cameraGeometry.js +1 -1
  38. package/src/app/controls/SimulatorViewControls.js +45 -0
  39. package/src/app/controls/workbenchBindings.js +64 -0
  40. package/src/app/controls/workbenchDisclosure.js +41 -0
  41. package/src/app/domBindings.js +21 -13
  42. package/src/app/gridWalk/GridWalkController.js +180 -0
  43. package/src/app/gridWalk/gridWalkCamera.js +88 -0
  44. package/src/app/gridWalk/gridWalkControls.js +24 -0
  45. package/src/app/gridWalk/gridWalkCrew.js +246 -0
  46. package/src/app/gridWalk/gridWalkLabels.js +104 -0
  47. package/src/app/gridWalk/gridWalkLayout.js +46 -0
  48. package/src/app/gridWalk/gridWalkNavigation.js +99 -0
  49. package/src/app/gridWalk/gridWalkProjectBanner.js +43 -0
  50. package/src/app/gridWalk/gridWalkSession.js +50 -0
  51. package/src/app/layoutSupport.js +2 -0
  52. package/src/app/raceOverlayClearanceSupport.js +21 -7
  53. package/src/app/readouts/carOverviewRenderer.js +9 -4
  54. package/src/app/readouts/readoutFormatters.js +1 -8
  55. package/src/app/readouts/telemetryRenderer.js +1 -3
  56. package/src/app/readouts/timingTowerRenderer.js +48 -50
  57. package/src/app/rendering/appAssets.js +8 -10
  58. package/src/app/rendering/carRenderer.js +27 -79
  59. package/src/app/rendering/displayUtils.js +0 -6
  60. package/src/app/rendering/expertSensorRenderer.js +1 -1
  61. package/src/app/rendering/replayGhostRenderer.js +4 -1
  62. package/src/app/rendering/trackRenderer.js +2 -2
  63. package/src/app/runtime/frameLoop.js +18 -17
  64. package/src/app/runtime/pixiStartupLifecycle.js +94 -0
  65. package/src/app/runtime/runtimeTiming.js +4 -1
  66. package/src/app/runtime/runtimeVisibility.js +7 -2
  67. package/src/config/cssValues.js +1 -1
  68. package/src/config/defaultOptions.js +12 -173
  69. package/src/config/restartOptions.js +1 -0
  70. package/src/config/runtimeSeed.js +10 -0
  71. package/src/config/simulatorDefaults.js +106 -0
  72. package/src/config/telemetryModules.js +20 -0
  73. package/src/config/theme/cssTokens.js +85 -0
  74. package/src/config/theme/themeCss.js +72 -0
  75. package/src/config/theme/themeInputs.js +155 -0
  76. package/src/config/theme/themeResolver.js +172 -0
  77. package/src/config/theme/themeSelectors.js +69 -0
  78. package/src/config/theme/themeTokens.js +257 -0
  79. package/src/config/theme/themeUtils.js +15 -0
  80. package/src/config/themeOptions.js +14 -830
  81. package/src/config/timingGapMode.js +0 -2
  82. package/src/config/uiOptions.js +53 -0
  83. package/src/data/championship.js +16 -158
  84. package/src/data/championshipDefaults.js +87 -0
  85. package/src/data/driverData.js +10 -20
  86. package/src/data/entryIdentity.js +67 -0
  87. package/src/data/entryPresentation.js +23 -0
  88. package/src/data/entryTypes.d.ts +160 -0
  89. package/src/data/index.d.ts +13 -259
  90. package/src/data/ratingScale.js +14 -0
  91. package/src/data/teamTypes.d.ts +23 -0
  92. package/src/data/trackTypes.d.ts +101 -0
  93. package/src/data/vehicleData.js +12 -23
  94. package/src/environment/actions.js +3 -2
  95. package/src/environment/controllerLoop.js +57 -44
  96. package/src/environment/controllerScheduler.js +30 -0
  97. package/src/environment/driverObservations.js +260 -0
  98. package/src/environment/driverReset.js +51 -0
  99. package/src/environment/episode.js +0 -1
  100. package/src/environment/evaluation.js +6 -90
  101. package/src/environment/evaluationTracker.js +100 -0
  102. package/src/environment/events.js +23 -0
  103. package/src/environment/index.d.ts +21 -140
  104. package/src/environment/metrics.js +3 -17
  105. package/src/environment/observationSchema.js +104 -0
  106. package/src/environment/observationVector.js +16 -115
  107. package/src/environment/observations.js +16 -316
  108. package/src/environment/options.js +5 -24
  109. package/src/environment/renderFrames.js +21 -0
  110. package/src/environment/resetOptions.js +41 -0
  111. package/src/environment/results.js +125 -0
  112. package/src/environment/rewards.js +10 -16
  113. package/src/environment/runtime.js +17 -300
  114. package/src/environment/scenarioOptions.js +90 -0
  115. package/src/environment/scenarioPlacements.js +70 -0
  116. package/src/environment/scenarioPresets.js +78 -0
  117. package/src/environment/scenarios.js +5 -236
  118. package/src/environment/scratch.js +18 -0
  119. package/src/environment/sensorOptions.js +20 -0
  120. package/src/environment/sensors/index.js +2 -556
  121. package/src/environment/sensors/indexedRayBands.js +0 -18
  122. package/src/environment/sensors/nearbyCars.js +18 -15
  123. package/src/environment/sensors/opponentRadar.js +3 -2
  124. package/src/environment/sensors/pitConnectorProximity.js +20 -0
  125. package/src/environment/sensors/rayBandTrace.js +1 -37
  126. package/src/environment/sensors/rayBatch.js +81 -0
  127. package/src/environment/sensors/rayConfig.js +26 -0
  128. package/src/environment/sensors/rayGeometry.js +0 -8
  129. package/src/environment/sensors/rayGuards.js +0 -6
  130. package/src/environment/sensors/rayOutputs.js +75 -0
  131. package/src/environment/sensors/rayQuery.js +110 -0
  132. package/src/environment/sensors/rayScratch.js +146 -0
  133. package/src/environment/sensors/sensorTargets.js +12 -4
  134. package/src/environment/sensors/surfaceRays.js +6 -61
  135. package/src/environment/sensors/trackRays.js +7 -80
  136. package/src/environment/simulationSetup.js +30 -0
  137. package/src/environment/specs.js +15 -109
  138. package/src/index.d.ts +34 -259
  139. package/src/index.js +28 -6
  140. package/src/placeholder/index.js +1 -8
  141. package/src/rendering/displayLifecycle.js +6 -0
  142. package/src/rendering/proceduralTrackAsset.js +1 -1
  143. package/src/rendering/renderSnapshot.js +3 -0
  144. package/src/rendering/track/finishLineRenderer.js +1 -1
  145. package/src/rendering/track/gridRenderer.js +1 -1
  146. package/src/rendering/track/offsetStrokeSafety.js +2 -2
  147. package/src/rendering/track/pitLaneRenderer.js +1 -1
  148. package/src/rendering/track/trackMaterialRenderer.js +4 -4
  149. package/src/rendering/track/trackRenderConstants.js +0 -1
  150. package/src/rendering/track/trackRenderGeometry.js +1 -7
  151. package/src/simulation/collisionGeometry.js +5 -11
  152. package/src/simulation/driver/advancedPathControls.js +53 -0
  153. package/src/simulation/driver/advancedRacePace.js +112 -0
  154. package/src/simulation/driver/advancedRacingControls.js +61 -0
  155. package/src/simulation/driver/advancedRacingEnvelope.js +255 -0
  156. package/src/simulation/driver/advancedRacingSteering.js +76 -0
  157. package/src/simulation/driver/advancedRacingTiming.js +16 -0
  158. package/src/simulation/driver/arcadeRacePace.js +83 -0
  159. package/src/simulation/driver/arcadeRacingControls.js +111 -0
  160. package/src/simulation/driver/arcadeRacingEnvelope.js +63 -0
  161. package/src/simulation/driver/driverControlConstants.js +0 -2
  162. package/src/simulation/driver/driverController.js +14 -3
  163. package/src/simulation/driver/edgeRecovery.js +0 -1
  164. package/src/simulation/driver/racingControls.js +10 -406
  165. package/src/simulation/driver/racingDriverProfile.js +48 -0
  166. package/src/simulation/driver/racingLinePlan.js +48 -15
  167. package/src/simulation/driver/rejoinControls.js +58 -111
  168. package/src/simulation/driver/safetyCarControls.js +12 -0
  169. package/src/simulation/driver/trafficScan.js +89 -6
  170. package/src/simulation/pit/pitCarPlacement.js +29 -0
  171. package/src/simulation/pit/pitFlow.js +8 -30
  172. package/src/simulation/pit/pitIntent.js +4 -3
  173. package/src/simulation/pit/pitOccupancy.js +2 -2
  174. package/src/simulation/pit/pitQueue.js +2 -18
  175. package/src/simulation/pit/pitRouteMovement.js +1 -36
  176. package/src/simulation/pit/pitRouting.js +5 -81
  177. package/src/simulation/pit/pitService.js +3 -32
  178. package/src/simulation/pit/pitServiceConstants.js +1 -0
  179. package/src/simulation/pit/pitServiceProgress.js +34 -0
  180. package/src/simulation/pit/pitSnapshots.js +8 -4
  181. package/src/simulation/pit/pitState.js +1 -5
  182. package/src/simulation/race/classification.js +1 -8
  183. package/src/simulation/race/raceFinish.js +3 -8
  184. package/src/simulation/race/raceOrder.js +2 -13
  185. package/src/simulation/race/raceProgress.js +6 -20
  186. package/src/simulation/race/raceSetup.js +1 -1
  187. package/src/simulation/race/raceStep.js +1 -1
  188. package/src/simulation/race/redFlag.js +2 -4
  189. package/src/simulation/race/safetyCar.js +3 -8
  190. package/src/simulation/race/stalledDnf.js +2 -4
  191. package/src/simulation/raceSimulation.js +6 -38
  192. package/src/simulation/replay/replayGhosts.js +44 -32
  193. package/src/simulation/rules/collisionSteward.js +0 -14
  194. package/src/simulation/rules/penaltyActions.js +37 -0
  195. package/src/simulation/rules/penaltyLedger.js +8 -1
  196. package/src/simulation/rules/rulesReview.js +1 -7
  197. package/src/simulation/simMath.js +4 -0
  198. package/src/simulation/snapshots/raceSnapshots.js +30 -11
  199. package/src/simulation/timing/drsState.js +8 -0
  200. package/src/simulation/timing/drsTiming.js +2 -4
  201. package/src/simulation/timing/lapTelemetry.js +7 -4
  202. package/src/simulation/timing/sectorPerformance.js +3 -3
  203. package/src/simulation/timing/timingLines.js +12 -6
  204. package/src/simulation/track/pitLaneAccess.js +2 -2
  205. package/src/simulation/track/pitLaneLayout.js +4 -135
  206. package/src/simulation/track/pitLaneModel.js +70 -0
  207. package/src/simulation/track/pitLaneState.js +0 -1
  208. package/src/simulation/track/pitServiceLayout.js +121 -0
  209. package/src/simulation/track/proceduralTrack.js +77 -0
  210. package/src/simulation/track/spatialQueries.js +1 -25
  211. package/src/simulation/track/trackBuilder.js +54 -0
  212. package/src/simulation/track/trackConstants.js +0 -22
  213. package/src/simulation/track/trackMath.js +4 -29
  214. package/src/simulation/track/trackModel.js +4 -241
  215. package/src/simulation/track/trackModelCache.js +105 -0
  216. package/src/simulation/track/trackNearestQueries.js +1218 -0
  217. package/src/simulation/track/trackQueryConstants.js +3 -0
  218. package/src/simulation/track/trackQueryConstruction.js +368 -0
  219. package/src/simulation/track/trackQueryIndex.js +12 -1933
  220. package/src/simulation/track/trackQuerySegments.js +174 -0
  221. package/src/simulation/track/trackQueryStats.js +12 -0
  222. package/src/simulation/track/trackRayQueries.js +165 -0
  223. package/src/simulation/vehicle/advancedContactResponse.js +89 -0
  224. package/src/simulation/vehicle/advancedTireForces.js +92 -0
  225. package/src/simulation/vehicle/advancedVehicleModel.js +65 -0
  226. package/src/simulation/vehicle/advancedVehiclePhysics.js +127 -0
  227. package/src/simulation/vehicle/contactResolution.js +20 -6
  228. package/src/simulation/vehicle/mainTrackWheelSurface.js +2 -13
  229. package/src/simulation/vehicle/pitWheelSurface.js +0 -12
  230. package/src/simulation/vehicle/runoffResponse.js +2 -4
  231. package/src/simulation/vehicle/sampledWheelSurface.js +266 -0
  232. package/src/simulation/vehicle/vehicleGeometry.js +0 -56
  233. package/src/simulation/vehicle/vehicleKinematics.js +10 -0
  234. package/src/simulation/vehicle/vehiclePhysics.js +35 -194
  235. package/src/simulation/vehicle/vehicleSnapshots.js +13 -4
  236. package/src/simulation/vehicle/vehicleState.js +2 -1
  237. package/src/simulation/vehicle/wheelSurface.js +13 -432
  238. package/src/simulation/vehicle/wheelSurfaceCache.js +55 -0
  239. package/src/simulation/vehicle/wheelSurfaceSummary.js +101 -0
  240. package/src/styles.css +173 -19
  241. package/src/ui/bannerTemplates.js +4 -2
  242. package/src/ui/cameraControlsTemplate.js +4 -2
  243. package/src/ui/gridWalkTemplate.js +29 -0
  244. package/src/ui/htmlEscaping.js +8 -0
  245. package/src/ui/shellTemplate.js +3 -0
  246. package/src/ui/telemetryDrawerTemplate.js +4 -2
  247. package/src/ui/telemetryTemplates.js +3 -22
  248. package/src/ui/templateUtils.js +2 -8
  249. package/src/ui/viewControlsTemplate.js +21 -0
@@ -1,21 +1,9 @@
1
- import { normalizeAngle } from '../simMath.js';
2
- import { CENTERLINE_CONTROLS, TRACK } from './trackConstants.js';
3
- import { distance } from './trackMath.js';
4
- import { rawCenterPoint } from './centerlineSpline.js';
5
- import { generateCenterlineControls, generateFallbackCenterlineControls, generateSafeFallbackCenterlineControls, normalizeSeed } from './proceduralCenterline.js';
6
- import { isValidProceduralTrackModel } from './trackValidation.js';
7
- import { recalculateSampleGeometry } from './sampleGeometry.js';
8
- import { rotateSamplesToStandingStart, straightenStandingStartSamples } from './startStraight.js';
9
- import { prepareSampleIndexLut } from './spatialQueries.js';
10
- import { createTrackSectors } from './trackSectors.js';
11
- import { deriveDrsZones, normalizeDrsZone } from './drsZones.js';
12
- import { createPitLaneModel } from './pitLaneLayout.js';
13
- import { attachTrackQueryIndex, createTrackQueryIndex } from './trackQueryIndex.js';
14
- import { resolveProceduralTrackOptions } from './trackGenerationOptions.js';
15
- import { attachTrackSnapshotJsonSerializer } from './trackSnapshotJson.js';
16
-
17
1
  export { WORLD, TRACK } from './trackConstants.js';
2
+ export { normalizeAngle } from '../simMath.js';
18
3
  export { isInDrsZone } from './drsZones.js';
4
+ export { buildTrackModel } from './trackBuilder.js';
5
+ export { createProceduralTrack } from './proceduralTrack.js';
6
+ export { PROCEDURAL_TRACK_CACHE_MAX_ENTRIES } from './trackModelCache.js';
19
7
  export {
20
8
  nearestTrackState,
21
9
  offsetTrackPoint,
@@ -24,228 +12,3 @@ export {
24
12
  sampleHeadingAt,
25
13
  sampleHeadingCurvatureAtInto,
26
14
  } from './spatialQueries.js';
27
-
28
- const PROCEDURAL_TRACK_CACHE = new Map();
29
- const TRACK_MODEL_CACHE = new WeakMap();
30
-
31
- export function buildTrackModel(track = TRACK) {
32
- const canReuseCachedModel = track === TRACK || Object.isFrozen(track);
33
- const cached = canReuseCachedModel ? TRACK_MODEL_CACHE.get(track) : null;
34
- if (cached) return cached;
35
-
36
- const controls = track.centerlineControls ?? CENTERLINE_CONTROLS;
37
- const base = [];
38
- for (let index = 0; index <= track.sampleCount; index += 1) {
39
- base.push(rawCenterPoint(index / track.sampleCount, controls, track.curveInterpolation));
40
- }
41
-
42
- let totalLength = 0;
43
- const samples = base.map((point, index) => {
44
- if (index > 0) totalLength += distance(base[index - 1], point);
45
- return { ...point, distance: totalLength, heading: 0, normalX: 0, normalY: 0, curvature: 0 };
46
- });
47
- recalculateSampleGeometry(samples);
48
-
49
- const startStraightOptions = track.generationOptions?.startStraight;
50
- const rotatedSamples = rotateSamplesToStandingStart(samples, totalLength, startStraightOptions);
51
- const straightenedTrack = straightenStandingStartSamples(rotatedSamples, totalLength, startStraightOptions);
52
- const normalizedSamples = straightenedTrack.samples;
53
- totalLength = straightenedTrack.totalLength;
54
-
55
- const model = {
56
- ...track,
57
- centerlineControls: controls,
58
- length: totalLength,
59
- samples: normalizedSamples,
60
- sectors: createTrackSectors(totalLength),
61
- drsZones: (track.drsZones ?? deriveDrsZones(normalizedSamples, totalLength))
62
- .map((zone) => normalizeDrsZone(zone, totalLength)),
63
- };
64
- model.pitLane = track.pitLane?.enabled === false || track.generationOptions?.pitLane?.enabled === false
65
- ? null
66
- : createPitLaneModel(model);
67
- attachTrackQueryIndex(model, createTrackQueryIndex(model));
68
- prepareSampleIndexLut(model);
69
- attachTrackSnapshotJsonSerializer(model);
70
- if (canReuseCachedModel) {
71
- TRACK_MODEL_CACHE.set(track, freezeTrackModel(model));
72
- }
73
- return model;
74
- }
75
-
76
- export function createProceduralTrack(seed = Date.now(), options = {}) {
77
- const normalizedSeed = normalizeSeed(seed);
78
- const generationOptions = resolveProceduralTrackOptions(options);
79
- const cacheKey = `${normalizedSeed}:${generationOptions.cacheKey}`;
80
- const cached = PROCEDURAL_TRACK_CACHE.get(cacheKey);
81
- if (cached) return cached;
82
-
83
- for (let attempt = 0; attempt < generationOptions.attempts.primary; attempt += 1) {
84
- const candidateSeed = (normalizedSeed + Math.imul(attempt, 2654435761)) >>> 0;
85
- const candidate = {
86
- ...TRACK,
87
- name: `Generated GP ${candidateSeed.toString(36).toUpperCase().padStart(6, '0').slice(-6)}`,
88
- seed: candidateSeed,
89
- curveInterpolation: 'centripetal',
90
- centerlineControls: generateCenterlineControls(candidateSeed, generationOptions.shape),
91
- drsZones: null,
92
- generationOptions,
93
- ...(generationOptions.pitLane.enabled === false ? { pitLane: { enabled: false } } : {}),
94
- };
95
- const model = buildTrackModel(candidate);
96
- if (isValidProceduralTrackModel(model, generationOptions)) {
97
- const trackDefinition = {
98
- ...candidate,
99
- drsZones: model.drsZones.map(({ id, startRatio, endRatio }) => ({ id, startRatio, endRatio })),
100
- };
101
- return cacheProceduralTrackDefinition(cacheKey, trackDefinition);
102
- }
103
- }
104
-
105
- let fallback = null;
106
- let fallbackModel = null;
107
- for (let attempt = 0; attempt < generationOptions.attempts.fallback; attempt += 1) {
108
- const candidateSeed = (normalizedSeed ^ Math.imul(attempt + 1, 2246822519)) >>> 0;
109
- const candidate = {
110
- ...TRACK,
111
- name: `Generated GP ${normalizedSeed.toString(36).toUpperCase().padStart(6, '0').slice(-6)}`,
112
- seed: candidateSeed,
113
- curveInterpolation: 'centripetal',
114
- centerlineControls: generateFallbackCenterlineControls(candidateSeed, generationOptions.shape),
115
- drsZones: null,
116
- generationOptions,
117
- ...(generationOptions.pitLane.enabled === false ? { pitLane: { enabled: false } } : {}),
118
- };
119
- const model = buildTrackModel(candidate);
120
- if (isValidProceduralTrackModel(model, generationOptions)) {
121
- fallback = candidate;
122
- fallbackModel = model;
123
- break;
124
- }
125
- }
126
-
127
- if (!fallback || !fallbackModel) {
128
- fallback = {
129
- ...TRACK,
130
- name: `Generated GP ${normalizedSeed.toString(36).toUpperCase().padStart(6, '0').slice(-6)}`,
131
- seed: normalizedSeed,
132
- curveInterpolation: 'centripetal',
133
- centerlineControls: generateSafeFallbackCenterlineControls(normalizedSeed, generationOptions.shape),
134
- drsZones: null,
135
- generationOptions,
136
- ...(generationOptions.pitLane.enabled === false ? { pitLane: { enabled: false } } : {}),
137
- };
138
- fallbackModel = buildTrackModel(fallback);
139
- }
140
- const fallbackDefinition = {
141
- ...fallback,
142
- drsZones: fallbackModel.drsZones.map(({ id, startRatio, endRatio }) => ({ id, startRatio, endRatio })),
143
- };
144
- return cacheProceduralTrackDefinition(cacheKey, fallbackDefinition);
145
- }
146
-
147
- export { normalizeAngle };
148
-
149
- function cacheProceduralTrackDefinition(cacheKey, trackDefinition) {
150
- const frozen = deepFreeze(trackDefinition);
151
- PROCEDURAL_TRACK_CACHE.set(cacheKey, frozen);
152
- return frozen;
153
- }
154
-
155
- function freezeTrackModel(model) {
156
- freezeArrayItems(model.samples);
157
- freezeArrayItems(model.centerlineControls);
158
- freezeArrayItems(model.sectors);
159
- freezeArrayItems(model.drsZones);
160
- deepFreeze(model.pitLane);
161
- freezeTrackQueryIndex(model.queryIndex);
162
- return Object.freeze(model);
163
- }
164
-
165
- function freezeArrayItems(items) {
166
- if (!Array.isArray(items)) return;
167
- items.forEach((item) => deepFreeze(item));
168
- Object.freeze(items);
169
- }
170
-
171
- function deepFreeze(value) {
172
- if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value;
173
- Object.freeze(value);
174
- Object.values(value).forEach((child) => {
175
- deepFreeze(child);
176
- });
177
- return value;
178
- }
179
-
180
- function freezeTrackQueryIndex(index) {
181
- if (!index || typeof index !== 'object') return index;
182
- deepFreeze(index.bands);
183
- freezeCenterlineSegments(index.centerline);
184
- freezeSpatialGrid(index.grid);
185
- freezeSpatialGrid(index.segmentGrid);
186
- freezeArcBuckets(index.arcBuckets);
187
- freezePitQueryIndex(index.pit);
188
- return Object.freeze(index);
189
- }
190
-
191
- function freezeCenterlineSegments(centerline) {
192
- if (!centerline || typeof centerline !== 'object') return centerline;
193
- Object.entries(centerline).forEach(([key, value]) => {
194
- if (ArrayBuffer.isView(value)) {
195
- centerline[key] = Object.freeze(Array.from(value));
196
- }
197
- });
198
- return Object.freeze(centerline);
199
- }
200
-
201
- function freezeSpatialGrid(grid) {
202
- if (!grid || typeof grid !== 'object') return grid;
203
- deepFreeze(grid.bounds);
204
- if (grid.cells instanceof Map) {
205
- for (const cell of grid.cells.values()) Object.freeze(cell);
206
- grid.cells = readonlyMap(grid.cells);
207
- }
208
- return Object.freeze(grid);
209
- }
210
-
211
- function freezeArcBuckets(arcBuckets) {
212
- if (!arcBuckets || typeof arcBuckets !== 'object') return arcBuckets;
213
- if (Array.isArray(arcBuckets.buckets)) {
214
- arcBuckets.buckets.forEach((bucket) => Object.freeze(bucket));
215
- Object.freeze(arcBuckets.buckets);
216
- }
217
- return Object.freeze(arcBuckets);
218
- }
219
-
220
- function freezePitQueryIndex(pit) {
221
- if (!pit || typeof pit !== 'object') return pit;
222
- freezeSpatialGrid(pit.roadGrid);
223
- freezeSpatialGrid(pit.boxGrid);
224
- deepFreeze(pit.routes);
225
- freezeArrayItems(pit.roadSegments);
226
- freezeArrayItems(pit.boxCandidates);
227
- return Object.freeze(pit);
228
- }
229
-
230
- function readonlyMap(map) {
231
- return new Proxy(map, {
232
- get(target, property) {
233
- if (property === 'set' || property === 'delete' || property === 'clear') {
234
- return () => {
235
- throw new TypeError('Cannot mutate a cached track query index');
236
- };
237
- }
238
- const value = Reflect.get(target, property, target);
239
- return typeof value === 'function' ? value.bind(target) : value;
240
- },
241
- set() {
242
- throw new TypeError('Cannot mutate a cached track query index');
243
- },
244
- deleteProperty() {
245
- throw new TypeError('Cannot mutate a cached track query index');
246
- },
247
- defineProperty() {
248
- throw new TypeError('Cannot mutate a cached track query index');
249
- },
250
- });
251
- }
@@ -0,0 +1,105 @@
1
+ import { TRACK } from './trackConstants.js';
2
+
3
+ const PROCEDURAL_TRACK_CACHE = new Map();
4
+ const TRACK_MODEL_CACHE = new WeakMap();
5
+ export const PROCEDURAL_TRACK_CACHE_MAX_ENTRIES = 8;
6
+
7
+ export function getCachedTrackModel(track) {
8
+ return track === TRACK || Object.isFrozen(track) ? TRACK_MODEL_CACHE.get(track) : null;
9
+ }
10
+
11
+ export function cacheTrackModel(track, model) {
12
+ if (track === TRACK || Object.isFrozen(track)) TRACK_MODEL_CACHE.set(track, freezeTrackModel(model));
13
+ }
14
+
15
+ export function getCachedProceduralTrack(cacheKey) {
16
+ const cached = PROCEDURAL_TRACK_CACHE.get(cacheKey);
17
+ if (!cached) return null;
18
+ PROCEDURAL_TRACK_CACHE.delete(cacheKey);
19
+ PROCEDURAL_TRACK_CACHE.set(cacheKey, cached);
20
+ return cached;
21
+ }
22
+
23
+ export function cacheProceduralTrackDefinition(cacheKey, trackDefinition, model) {
24
+ const frozen = deepFreeze(trackDefinition);
25
+ if (model) TRACK_MODEL_CACHE.set(frozen, freezeTrackModel(model));
26
+ while (PROCEDURAL_TRACK_CACHE.size >= PROCEDURAL_TRACK_CACHE_MAX_ENTRIES) {
27
+ const oldestKey = PROCEDURAL_TRACK_CACHE.keys().next().value;
28
+ if (oldestKey == null) break;
29
+ PROCEDURAL_TRACK_CACHE.delete(oldestKey);
30
+ }
31
+ PROCEDURAL_TRACK_CACHE.set(cacheKey, frozen);
32
+ return frozen;
33
+ }
34
+
35
+ function freezeTrackModel(model) {
36
+ freezeArrayItems(model.samples);
37
+ freezeArrayItems(model.centerlineControls);
38
+ freezeArrayItems(model.sectors);
39
+ freezeArrayItems(model.drsZones);
40
+ deepFreeze(model.pitLane);
41
+ freezeTrackQueryIndex(model.queryIndex);
42
+ return Object.freeze(model);
43
+ }
44
+
45
+ function freezeArrayItems(items) {
46
+ if (!Array.isArray(items)) return;
47
+ items.forEach((item) => deepFreeze(item));
48
+ Object.freeze(items);
49
+ }
50
+
51
+ function deepFreeze(value) {
52
+ if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value;
53
+ Object.freeze(value);
54
+ Object.values(value).forEach((child) => {
55
+ deepFreeze(child);
56
+ });
57
+ return value;
58
+ }
59
+
60
+ function freezeTrackQueryIndex(index) {
61
+ if (!index || typeof index !== 'object') return index;
62
+ deepFreeze(index.bands);
63
+ freezeCenterlineSegments(index.centerline);
64
+ freezeSpatialGrid(index.grid);
65
+ freezeSpatialGrid(index.segmentGrid);
66
+ freezeArcBuckets(index.arcBuckets);
67
+ freezePitQueryIndex(index.pit);
68
+ return Object.freeze(index);
69
+ }
70
+
71
+ function freezeCenterlineSegments(centerline) {
72
+ if (!centerline || typeof centerline !== 'object') return centerline;
73
+ Object.entries(centerline).forEach(([key, value]) => {
74
+ if (ArrayBuffer.isView(value)) {
75
+ centerline[key] = Object.freeze(Array.from(value));
76
+ }
77
+ });
78
+ return Object.freeze(centerline);
79
+ }
80
+
81
+ function freezeSpatialGrid(grid) {
82
+ if (!grid || typeof grid !== 'object') return grid;
83
+ deepFreeze(grid.bounds);
84
+ freezeArrayItems(grid.cells);
85
+ return Object.freeze(grid);
86
+ }
87
+
88
+ function freezeArcBuckets(arcBuckets) {
89
+ if (!arcBuckets || typeof arcBuckets !== 'object') return arcBuckets;
90
+ if (Array.isArray(arcBuckets.buckets)) {
91
+ arcBuckets.buckets.forEach((bucket) => Object.freeze(bucket));
92
+ Object.freeze(arcBuckets.buckets);
93
+ }
94
+ return Object.freeze(arcBuckets);
95
+ }
96
+
97
+ function freezePitQueryIndex(pit) {
98
+ if (!pit || typeof pit !== 'object') return pit;
99
+ freezeSpatialGrid(pit.roadGrid);
100
+ freezeSpatialGrid(pit.boxGrid);
101
+ deepFreeze(pit.routes);
102
+ freezeArrayItems(pit.roadSegments);
103
+ freezeArrayItems(pit.boxCandidates);
104
+ return Object.freeze(pit);
105
+ }