@inventure71/paddockjs 4.1.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 (257) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/INSTALL_AND_UPDATE.md +31 -5
  3. package/README.md +34 -3
  4. package/docs/advanced_ai_pace.md +131 -0
  5. package/docs/advanced_physics_rebuild.md +166 -0
  6. package/docs/architecture.md +111 -30
  7. package/docs/cleanup_coverage.md +284 -0
  8. package/docs/component_inventory.md +32 -16
  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/getting-started.md +3 -0
  16. package/docs/grid_walk.md +80 -0
  17. package/docs/index.md +5 -2
  18. package/docs/keyboard_driving_fix.md +118 -0
  19. package/docs/limit_driving_2026-10-14.md +88 -0
  20. package/docs/loading.md +141 -0
  21. package/docs/maintainability_audit_2026-09-08.md +146 -0
  22. package/docs/racing_ai_improvements.md +145 -0
  23. package/docs/rules.md +43 -13
  24. package/docs/sense_contract.md +4 -1
  25. package/docs/system_specs.md +40 -12
  26. package/docs/testing-reliability-guide.md +67 -0
  27. package/docs/v11_improvements.md +108 -0
  28. package/package.json +23 -15
  29. package/src/api/MountedSimulatorComponents.js +101 -0
  30. package/src/api/PaddockSimulatorController.js +56 -81
  31. package/src/app/BrowserExpertAdapter.js +0 -6
  32. package/src/app/BrowserExpertExternalRenderer.js +72 -50
  33. package/src/app/ExternalTrackSnapshot.js +50 -0
  34. package/src/app/F1SimulatorApp.js +242 -297
  35. package/src/app/HostLifecycleEmitter.js +93 -0
  36. package/src/app/banners/raceDataBannerController.js +9 -4
  37. package/src/app/camera/cameraConstants.js +1 -1
  38. package/src/app/camera/cameraController.js +10 -13
  39. package/src/app/camera/cameraGeometry.js +1 -1
  40. package/src/app/controls/SimulatorViewControls.js +45 -0
  41. package/src/app/controls/workbenchBindings.js +64 -0
  42. package/src/app/controls/workbenchDisclosure.js +41 -0
  43. package/src/app/domBindings.js +21 -13
  44. package/src/app/gridWalk/GridWalkController.js +180 -0
  45. package/src/app/gridWalk/gridWalkCamera.js +88 -0
  46. package/src/app/gridWalk/gridWalkControls.js +24 -0
  47. package/src/app/gridWalk/gridWalkCrew.js +246 -0
  48. package/src/app/gridWalk/gridWalkLabels.js +104 -0
  49. package/src/app/gridWalk/gridWalkLayout.js +46 -0
  50. package/src/app/gridWalk/gridWalkNavigation.js +99 -0
  51. package/src/app/gridWalk/gridWalkProjectBanner.js +43 -0
  52. package/src/app/gridWalk/gridWalkSession.js +50 -0
  53. package/src/app/layoutSupport.js +2 -0
  54. package/src/app/raceOverlayClearanceSupport.js +21 -7
  55. package/src/app/readouts/carOverviewRenderer.js +9 -4
  56. package/src/app/readouts/readoutFormatters.js +1 -8
  57. package/src/app/readouts/telemetryRenderer.js +1 -3
  58. package/src/app/readouts/timingTowerRenderer.js +48 -50
  59. package/src/app/rendering/appAssets.js +8 -10
  60. package/src/app/rendering/carRenderer.js +27 -79
  61. package/src/app/rendering/displayUtils.js +0 -6
  62. package/src/app/rendering/expertSensorRenderer.js +1 -1
  63. package/src/app/rendering/replayGhostRenderer.js +4 -1
  64. package/src/app/rendering/trackRenderer.js +2 -2
  65. package/src/app/runtime/frameLoop.js +18 -17
  66. package/src/app/runtime/pixiStartupLifecycle.js +94 -0
  67. package/src/app/runtime/runtimeTiming.js +4 -1
  68. package/src/app/runtime/runtimeVisibility.js +7 -2
  69. package/src/config/cssValues.js +1 -1
  70. package/src/config/defaultOptions.js +12 -173
  71. package/src/config/restartOptions.js +1 -0
  72. package/src/config/runtimeSeed.js +10 -0
  73. package/src/config/simulatorDefaults.js +106 -0
  74. package/src/config/telemetryModules.js +20 -0
  75. package/src/config/theme/cssTokens.js +85 -0
  76. package/src/config/theme/themeCss.js +72 -0
  77. package/src/config/theme/themeInputs.js +155 -0
  78. package/src/config/theme/themeResolver.js +172 -0
  79. package/src/config/theme/themeSelectors.js +69 -0
  80. package/src/config/theme/themeTokens.js +257 -0
  81. package/src/config/theme/themeUtils.js +15 -0
  82. package/src/config/themeOptions.js +14 -830
  83. package/src/config/timingGapMode.js +0 -2
  84. package/src/config/uiOptions.js +53 -0
  85. package/src/data/championship.js +16 -158
  86. package/src/data/championshipDefaults.js +87 -0
  87. package/src/data/driverData.js +10 -20
  88. package/src/data/entryIdentity.js +67 -0
  89. package/src/data/entryPresentation.js +23 -0
  90. package/src/data/entryTypes.d.ts +160 -0
  91. package/src/data/index.d.ts +12 -195
  92. package/src/data/ratingScale.js +14 -0
  93. package/src/data/teamTypes.d.ts +23 -0
  94. package/src/data/trackTypes.d.ts +101 -0
  95. package/src/data/vehicleData.js +12 -23
  96. package/src/environment/actions.js +3 -2
  97. package/src/environment/controllerLoop.js +57 -44
  98. package/src/environment/controllerScheduler.js +30 -0
  99. package/src/environment/driverObservations.js +260 -0
  100. package/src/environment/driverReset.js +51 -0
  101. package/src/environment/episode.js +0 -1
  102. package/src/environment/evaluation.js +6 -90
  103. package/src/environment/evaluationTracker.js +100 -0
  104. package/src/environment/events.js +23 -0
  105. package/src/environment/index.d.ts +35 -91
  106. package/src/environment/metrics.js +3 -17
  107. package/src/environment/observationSchema.js +104 -0
  108. package/src/environment/observationVector.js +16 -115
  109. package/src/environment/observations.js +16 -316
  110. package/src/environment/options.js +5 -24
  111. package/src/environment/renderFrames.js +21 -0
  112. package/src/environment/resetOptions.js +41 -0
  113. package/src/environment/results.js +125 -0
  114. package/src/environment/rewards.js +10 -16
  115. package/src/environment/runtime.js +17 -300
  116. package/src/environment/scenarioOptions.js +90 -0
  117. package/src/environment/scenarioPlacements.js +70 -0
  118. package/src/environment/scenarioPresets.js +78 -0
  119. package/src/environment/scenarios.js +5 -236
  120. package/src/environment/scratch.js +18 -0
  121. package/src/environment/sensorOptions.js +20 -0
  122. package/src/environment/sensors/index.js +2 -556
  123. package/src/environment/sensors/indexedRayBands.js +0 -18
  124. package/src/environment/sensors/nearbyCars.js +18 -15
  125. package/src/environment/sensors/opponentRadar.js +3 -2
  126. package/src/environment/sensors/pitConnectorProximity.js +20 -0
  127. package/src/environment/sensors/rayBandTrace.js +1 -37
  128. package/src/environment/sensors/rayBatch.js +81 -0
  129. package/src/environment/sensors/rayConfig.js +26 -0
  130. package/src/environment/sensors/rayGeometry.js +0 -8
  131. package/src/environment/sensors/rayGuards.js +0 -6
  132. package/src/environment/sensors/rayOutputs.js +75 -0
  133. package/src/environment/sensors/rayQuery.js +110 -0
  134. package/src/environment/sensors/rayScratch.js +146 -0
  135. package/src/environment/sensors/sensorTargets.js +12 -4
  136. package/src/environment/sensors/surfaceRays.js +6 -61
  137. package/src/environment/sensors/trackRays.js +7 -80
  138. package/src/environment/simulationSetup.js +30 -0
  139. package/src/environment/specs.js +15 -109
  140. package/src/index.d.ts +43 -202
  141. package/src/index.js +28 -6
  142. package/src/placeholder/index.js +1 -8
  143. package/src/rendering/displayLifecycle.js +6 -0
  144. package/src/rendering/proceduralTrackAsset.js +1 -1
  145. package/src/rendering/renderSnapshot.js +3 -0
  146. package/src/rendering/track/finishLineRenderer.js +1 -1
  147. package/src/rendering/track/gridRenderer.js +1 -1
  148. package/src/rendering/track/offsetStrokeSafety.js +2 -2
  149. package/src/rendering/track/pitLaneRenderer.js +1 -1
  150. package/src/rendering/track/trackMaterialRenderer.js +4 -4
  151. package/src/rendering/track/trackRenderConstants.js +0 -1
  152. package/src/rendering/track/trackRenderGeometry.js +1 -7
  153. package/src/simulation/collisionGeometry.js +12 -20
  154. package/src/simulation/driver/advancedPathControls.js +53 -0
  155. package/src/simulation/driver/advancedRacePace.js +112 -0
  156. package/src/simulation/driver/advancedRacingControls.js +61 -0
  157. package/src/simulation/driver/advancedRacingEnvelope.js +255 -0
  158. package/src/simulation/driver/advancedRacingSteering.js +76 -0
  159. package/src/simulation/driver/advancedRacingTiming.js +16 -0
  160. package/src/simulation/driver/arcadeRacePace.js +83 -0
  161. package/src/simulation/driver/arcadeRacingControls.js +111 -0
  162. package/src/simulation/driver/arcadeRacingEnvelope.js +63 -0
  163. package/src/simulation/driver/driverControlConstants.js +0 -2
  164. package/src/simulation/driver/driverController.js +16 -5
  165. package/src/simulation/driver/edgeRecovery.js +2 -3
  166. package/src/simulation/driver/racingControls.js +10 -396
  167. package/src/simulation/driver/racingDriverProfile.js +48 -0
  168. package/src/simulation/driver/racingLinePlan.js +54 -20
  169. package/src/simulation/driver/rejoinControls.js +59 -112
  170. package/src/simulation/driver/safetyCarControls.js +12 -0
  171. package/src/simulation/driver/trafficScan.js +136 -27
  172. package/src/simulation/participants/participantInteractions.js +24 -15
  173. package/src/simulation/pit/pitCarPlacement.js +29 -0
  174. package/src/simulation/pit/pitFlow.js +8 -30
  175. package/src/simulation/pit/pitIntent.js +4 -3
  176. package/src/simulation/pit/pitOccupancy.js +2 -2
  177. package/src/simulation/pit/pitQueue.js +2 -18
  178. package/src/simulation/pit/pitRouteMovement.js +1 -36
  179. package/src/simulation/pit/pitRouting.js +5 -81
  180. package/src/simulation/pit/pitService.js +3 -32
  181. package/src/simulation/pit/pitServiceConstants.js +1 -0
  182. package/src/simulation/pit/pitServiceProgress.js +34 -0
  183. package/src/simulation/pit/pitSnapshots.js +8 -4
  184. package/src/simulation/pit/pitState.js +1 -5
  185. package/src/simulation/race/classification.js +1 -8
  186. package/src/simulation/race/raceFinish.js +3 -8
  187. package/src/simulation/race/raceOrder.js +2 -13
  188. package/src/simulation/race/raceProgress.js +6 -20
  189. package/src/simulation/race/raceSetup.js +1 -1
  190. package/src/simulation/race/raceStep.js +20 -11
  191. package/src/simulation/race/redFlag.js +2 -4
  192. package/src/simulation/race/safetyCar.js +5 -9
  193. package/src/simulation/race/stalledDnf.js +2 -4
  194. package/src/simulation/raceSimulation.js +6 -38
  195. package/src/simulation/replay/replayGhosts.js +44 -32
  196. package/src/simulation/rules/collisionSteward.js +0 -14
  197. package/src/simulation/rules/penaltyActions.js +37 -0
  198. package/src/simulation/rules/penaltyLedger.js +8 -1
  199. package/src/simulation/rules/pitLaneSpeedingSteward.js +1 -1
  200. package/src/simulation/rules/rulesReview.js +20 -16
  201. package/src/simulation/rules/trackLimitsSteward.js +2 -2
  202. package/src/simulation/simMath.js +4 -0
  203. package/src/simulation/snapshots/raceSnapshots.js +30 -11
  204. package/src/simulation/timing/drsState.js +8 -0
  205. package/src/simulation/timing/drsTiming.js +2 -4
  206. package/src/simulation/timing/lapTelemetry.js +7 -4
  207. package/src/simulation/timing/sectorPerformance.js +14 -6
  208. package/src/simulation/timing/timingLines.js +12 -6
  209. package/src/simulation/track/pitLaneAccess.js +2 -2
  210. package/src/simulation/track/pitLaneLayout.js +4 -135
  211. package/src/simulation/track/pitLaneModel.js +70 -0
  212. package/src/simulation/track/pitLaneState.js +0 -1
  213. package/src/simulation/track/pitServiceLayout.js +121 -0
  214. package/src/simulation/track/proceduralTrack.js +77 -0
  215. package/src/simulation/track/spatialQueries.js +76 -31
  216. package/src/simulation/track/trackBuilder.js +54 -0
  217. package/src/simulation/track/trackConstants.js +0 -22
  218. package/src/simulation/track/trackMath.js +4 -29
  219. package/src/simulation/track/trackModel.js +12 -240
  220. package/src/simulation/track/trackModelCache.js +105 -0
  221. package/src/simulation/track/trackNearestQueries.js +1218 -0
  222. package/src/simulation/track/trackQueryConstants.js +3 -0
  223. package/src/simulation/track/trackQueryConstruction.js +368 -0
  224. package/src/simulation/track/trackQueryIndex.js +12 -1933
  225. package/src/simulation/track/trackQuerySegments.js +174 -0
  226. package/src/simulation/track/trackQueryStats.js +12 -0
  227. package/src/simulation/track/trackRayQueries.js +165 -0
  228. package/src/simulation/vehicle/advancedContactResponse.js +89 -0
  229. package/src/simulation/vehicle/advancedTireForces.js +92 -0
  230. package/src/simulation/vehicle/advancedVehicleModel.js +65 -0
  231. package/src/simulation/vehicle/advancedVehiclePhysics.js +127 -0
  232. package/src/simulation/vehicle/contactResolution.js +23 -9
  233. package/src/simulation/vehicle/mainTrackWheelSurface.js +2 -13
  234. package/src/simulation/vehicle/pitWheelSurface.js +0 -12
  235. package/src/simulation/vehicle/runoffResponse.js +2 -4
  236. package/src/simulation/vehicle/sampledWheelSurface.js +266 -0
  237. package/src/simulation/vehicle/vehicleGeometry.js +85 -103
  238. package/src/simulation/vehicle/vehicleKinematics.js +10 -0
  239. package/src/simulation/vehicle/vehiclePhysics.js +42 -196
  240. package/src/simulation/vehicle/vehicleSnapshots.js +18 -6
  241. package/src/simulation/vehicle/vehicleState.js +5 -3
  242. package/src/simulation/vehicle/wheelSurface.js +13 -432
  243. package/src/simulation/vehicle/wheelSurfaceCache.js +55 -0
  244. package/src/simulation/vehicle/wheelSurfaceSummary.js +101 -0
  245. package/src/styles.css +173 -19
  246. package/src/ui/bannerTemplates.js +4 -2
  247. package/src/ui/cameraControlsTemplate.js +4 -2
  248. package/src/ui/gridWalkTemplate.js +29 -0
  249. package/src/ui/htmlEscaping.js +8 -0
  250. package/src/ui/raceCanvasTemplate.js +3 -2
  251. package/src/ui/shellTemplate.js +3 -0
  252. package/src/ui/telemetryDrawerTemplate.js +4 -2
  253. package/src/ui/telemetryTemplates.js +3 -22
  254. package/src/ui/templateUtils.js +2 -8
  255. package/src/ui/viewControlsTemplate.js +21 -0
  256. package/examples/train-basic-policy.mjs +0 -276
  257. package/examples/trainingData.mjs +0 -133
@@ -1,29 +1,8 @@
1
- import { normalizeAngle } from '../simulation/simMath.js';
2
- import { offsetTrackPoint, pointAt } from '../simulation/track/trackModel.js';
3
- import { kphToSimSpeed, metersToSimUnits, simUnitsToMeters } from '../simulation/units.js';
4
-
5
- export const ENVIRONMENT_SCENARIO_PRESETS = Object.freeze([
6
- 'cornering',
7
- 'off-track-recovery',
8
- 'overtaking-pack',
9
- 'pit-entry',
10
- ]);
11
-
12
- export function resolveScenarioPlacementConfig(scenario = {}, driverIds = new Set()) {
13
- const preset = scenario.preset == null ? null : String(scenario.preset);
14
- if (preset && !ENVIRONMENT_SCENARIO_PRESETS.includes(preset)) {
15
- throw new Error(`PaddockJS environment scenario.preset must be one of: ${ENVIRONMENT_SCENARIO_PRESETS.join(', ')}.`);
16
- }
17
-
18
- const placements = normalizePlacementMap(scenario.placements, driverIds);
19
- const traffic = normalizeTrafficLayout(scenario.traffic, driverIds);
20
-
21
- return {
22
- preset,
23
- placements,
24
- traffic,
25
- };
26
- }
1
+ import { applyEnvironmentPlacements } from './scenarioPlacements.js';
2
+ import { buildPresetPlacements } from './scenarioPresets.js';
3
+ export { ENVIRONMENT_SCENARIO_PRESETS } from './scenarioPresets.js';
4
+ export { resolveScenarioPlacementConfig, normalizeEnvironmentPlacements } from './scenarioOptions.js';
5
+ export { applyEnvironmentPlacements } from './scenarioPlacements.js';
27
6
 
28
7
  export function applyEnvironmentScenario(sim, options) {
29
8
  const scenario = options.scenario ?? {};
@@ -56,213 +35,3 @@ export function applyEnvironmentScenario(sim, options) {
56
35
  });
57
36
  });
58
37
  }
59
-
60
- export function normalizeEnvironmentPlacements(placements = {}, driverIds = new Set()) {
61
- return normalizePlacementMap(placements, driverIds);
62
- }
63
-
64
- function normalizePlacementMap(value, driverIds) {
65
- if (value == null) return {};
66
- if (typeof value !== 'object' || Array.isArray(value)) {
67
- throw new Error('PaddockJS environment scenario.placements must be an object keyed by driver id.');
68
- }
69
- return Object.fromEntries(Object.entries(value).map(([driverId, placement]) => {
70
- assertKnownDriver(driverId, driverIds, 'scenario placement');
71
- return [driverId, normalizePlacement(placement, `scenario.placements.${driverId}`)];
72
- }));
73
- }
74
-
75
- function normalizeTrafficLayout(value, driverIds) {
76
- if (value == null) return [];
77
- if (!Array.isArray(value)) {
78
- throw new Error('PaddockJS environment scenario.traffic must be an array.');
79
- }
80
- return value.map((entry, index) => {
81
- if (!entry || typeof entry !== 'object') {
82
- throw new Error(`PaddockJS environment scenario.traffic[${index}] must be an object.`);
83
- }
84
- assertKnownDriver(entry.driverId, driverIds, `scenario.traffic[${index}].driverId`);
85
- assertKnownDriver(entry.relativeTo, driverIds, `scenario.traffic[${index}].relativeTo`);
86
- return {
87
- driverId: entry.driverId,
88
- relativeTo: entry.relativeTo,
89
- deltaDistanceMeters: finiteNumber(entry.deltaDistanceMeters, 0, `scenario.traffic[${index}].deltaDistanceMeters`),
90
- offsetMeters: finiteNumber(entry.offsetMeters, 0, `scenario.traffic[${index}].offsetMeters`),
91
- speedKph: optionalFiniteNumber(entry.speedKph, `scenario.traffic[${index}].speedKph`),
92
- headingErrorRadians: finiteNumber(entry.headingErrorRadians, 0, `scenario.traffic[${index}].headingErrorRadians`),
93
- };
94
- });
95
- }
96
-
97
- function normalizePlacement(placement, label) {
98
- if (!placement || typeof placement !== 'object') {
99
- throw new Error(`PaddockJS environment ${label} must be an object.`);
100
- }
101
- const distanceMeters = placement.distanceMeters ?? placement.startDistanceMeters;
102
- return {
103
- distanceMeters: optionalFiniteNumber(distanceMeters, `${label}.distanceMeters`),
104
- offsetMeters: finiteNumber(placement.offsetMeters, 0, `${label}.offsetMeters`),
105
- speedKph: optionalFiniteNumber(placement.speedKph, `${label}.speedKph`),
106
- headingErrorRadians: finiteNumber(placement.headingErrorRadians, 0, `${label}.headingErrorRadians`),
107
- };
108
- }
109
-
110
- function buildPresetPlacements(snapshot, options) {
111
- const preset = options.scenario?.preset;
112
- if (!preset) return {};
113
- const controlledDriverId = options.controlledDrivers[0];
114
- const cars = snapshot.cars;
115
- const track = snapshot.track;
116
- const primaryDistance = presetBaseDistance(track, preset);
117
- const placements = {};
118
-
119
- if (preset === 'cornering') {
120
- placements[controlledDriverId] = {
121
- distanceMeters: simUnitsToMeters(primaryDistance),
122
- offsetMeters: 0,
123
- speedKph: 135,
124
- headingErrorRadians: 0,
125
- };
126
- } else if (preset === 'off-track-recovery') {
127
- placements[controlledDriverId] = {
128
- distanceMeters: simUnitsToMeters(primaryDistance),
129
- offsetMeters: simUnitsToMeters(track.width / 2) + 7,
130
- speedKph: 52,
131
- headingErrorRadians: -0.45,
132
- };
133
- } else if (preset === 'pit-entry') {
134
- placements[controlledDriverId] = {
135
- distanceMeters: simUnitsToMeters(primaryDistance),
136
- offsetMeters: 0,
137
- speedKph: 120,
138
- headingErrorRadians: 0,
139
- };
140
- } else if (preset === 'overtaking-pack') {
141
- const baseMeters = simUnitsToMeters(primaryDistance);
142
- cars.slice(0, 4).forEach((car, index) => {
143
- placements[car.id] = {
144
- distanceMeters: baseMeters + (index - 1) * 18,
145
- offsetMeters: index % 2 === 0 ? -2.2 : 2.2,
146
- speedKph: 135 - index * 3,
147
- headingErrorRadians: 0,
148
- };
149
- });
150
- }
151
-
152
- return placements;
153
- }
154
-
155
- function presetBaseDistance(track, preset) {
156
- if (preset === 'cornering') return findHighCurvatureDistance(track) - metersToSimUnits(70);
157
- if (preset === 'off-track-recovery') return track.length * 0.42;
158
- if (preset === 'overtaking-pack') return track.length * 0.35;
159
- if (preset === 'pit-entry') {
160
- const pitDistance = track.pitLane?.entry?.trackDistance ??
161
- track.pitLane?.entry?.distanceFromStart ??
162
- track.length * 0.92;
163
- return pitDistance - metersToSimUnits(170);
164
- }
165
- return 0;
166
- }
167
-
168
- function findHighCurvatureDistance(track) {
169
- const samples = Array.isArray(track.samples) ? track.samples : [];
170
- const fallback = track.length * 0.25;
171
- const best = samples
172
- .filter((sample) => sample.distance > metersToSimUnits(350))
173
- .reduce((selected, sample) => {
174
- if (!selected || (sample.curvature ?? 0) > (selected.curvature ?? 0)) return sample;
175
- return selected;
176
- }, null);
177
- return best?.distance ?? fallback;
178
- }
179
-
180
- export function applyEnvironmentPlacements(sim, track, carsById, placements = {}) {
181
- const states = {};
182
- Object.entries(placements).forEach(([driverId, placement]) => {
183
- const currentCar = carsById.get(driverId);
184
- if (!currentCar) return;
185
- states[driverId] = placementToCarState(track, currentCar, placement, {
186
- physicsMode: sim.physicsMode,
187
- });
188
- });
189
- if (typeof sim.setCarStates === 'function') {
190
- sim.setCarStates(states);
191
- return;
192
- }
193
- Object.entries(states).forEach(([driverId, partial]) => {
194
- sim.setCarState(driverId, partial);
195
- });
196
- }
197
-
198
- function placementToCarState(track, currentCar, placement, { physicsMode = 'arcade' } = {}) {
199
- const currentDistanceMeters = currentCar.distanceMeters ?? simUnitsToMeters(currentCar.raceDistance ?? currentCar.progress ?? 0);
200
- const raceDistance = metersToSimUnits(Math.max(0, placement.distanceMeters ?? currentDistanceMeters));
201
- const offset = metersToSimUnits(placement.offsetMeters ?? 0);
202
- const base = pointAt(track, raceDistance);
203
- const position = offsetTrackPoint(base, offset);
204
- const heading = normalizeAngle(base.heading + (placement.headingErrorRadians ?? 0));
205
- const speed = placement.speedKph == null ? null : kphToSimSpeed(placement.speedKph);
206
- const partial = {
207
- x: position.x,
208
- y: position.y,
209
- previousX: position.x,
210
- previousY: position.y,
211
- progress: base.distance,
212
- raceDistance,
213
- desiredOffset: offset,
214
- heading,
215
- previousHeading: heading,
216
- destroyed: false,
217
- destroyReason: null,
218
- destroyedAt: null,
219
- outOfRace: false,
220
- canAttack: true,
221
- throttle: 0,
222
- brake: 0,
223
- longitudinalAcceleration: 0,
224
- lateralAcceleration: 0,
225
- lateralG: 0,
226
- longitudinalG: 0,
227
- gripUsage: 0,
228
- slipAngleRadians: 0,
229
- tractionLimited: false,
230
- steeringAngle: 0,
231
- yawRate: 0,
232
- stabilityState: 'stable',
233
- };
234
- if (speed != null) {
235
- partial.speed = speed;
236
- if (physicsMode === 'advanced') {
237
- partial.velocityX = Math.cos(heading) * speed;
238
- partial.velocityY = Math.sin(heading) * speed;
239
- } else {
240
- partial.velocityX = null;
241
- partial.velocityY = null;
242
- }
243
- }
244
- return partial;
245
- }
246
-
247
- function assertKnownDriver(driverId, driverIds, label) {
248
- if (typeof driverId !== 'string' || driverId.length === 0) {
249
- throw new Error(`PaddockJS environment ${label} must be a driver id.`);
250
- }
251
- if (!driverIds.has(driverId)) {
252
- throw new Error(`PaddockJS environment ${label} does not exist: ${driverId}`);
253
- }
254
- }
255
-
256
- function finiteNumber(value, fallback, label) {
257
- if (value == null) return fallback;
258
- const number = Number(value);
259
- if (!Number.isFinite(number)) {
260
- throw new Error(`PaddockJS environment ${label} must be a finite number.`);
261
- }
262
- return number;
263
- }
264
-
265
- function optionalFiniteNumber(value, label) {
266
- if (value == null) return undefined;
267
- return finiteNumber(value, undefined, label);
268
- }
@@ -0,0 +1,18 @@
1
+ import { clearRayBatchContextScratch } from './sensors/rayScratch.js';
2
+
3
+ export function clearObservationScratch(scratch) {
4
+ if (!scratch) return;
5
+ scratch.carsById?.clear?.();
6
+ scratch.eventsByDriver?.clear?.();
7
+ scratch.controlledEventDrivers?.clear?.();
8
+ clearRayBatchContextScratch(scratch.rayBatchContextScratch);
9
+ if (Array.isArray(scratch.eventDriverIds)) scratch.eventDriverIds.length = 0;
10
+ }
11
+
12
+ export function clearMetricScratch(scratch) {
13
+ if (!scratch) return;
14
+ scratch.previousCarsById?.clear?.();
15
+ scratch.currentCarsById?.clear?.();
16
+ scratch.contactCounts?.clear?.();
17
+ if (Array.isArray(scratch.eventDriverIds)) scratch.eventDriverIds.length = 0;
18
+ }
@@ -0,0 +1,20 @@
1
+ import { mergeRayOptionOverrides, normalizeRayOptions } from './sensors/rayConfig.js';
2
+
3
+ export function defaultSensorOptions(options) {
4
+ return {
5
+ rays: normalizeRayOptions(options.sensors.rays),
6
+ nearbyCars: options.sensors.nearbyCars,
7
+ };
8
+ }
9
+
10
+ export function effectiveSensorOptions(options, driverId, defaultSensors = defaultSensorOptions(options), hasSensorOverrides = true) {
11
+ if (!hasSensorOverrides || !options.sensorsByDriver?.[driverId]) return defaultSensors;
12
+ const rayOverrides = options.sensorsByDriver?.[driverId]?.rays ?? {};
13
+ return {
14
+ rays: normalizeRayOptions(mergeRayOptionOverrides(options.sensors.rays, rayOverrides)),
15
+ nearbyCars: {
16
+ ...options.sensors.nearbyCars,
17
+ ...(options.sensorsByDriver?.[driverId]?.nearbyCars ?? {}),
18
+ },
19
+ };
20
+ }