@inventure71/paddockjs 3.0.0 → 4.0.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 (90) hide show
  1. package/CHANGELOG.md +146 -1
  2. package/INSTALL_AND_UPDATE.md +15 -4
  3. package/README.md +84 -562
  4. package/docs/architecture.md +28 -18
  5. package/docs/composable-layouts.md +77 -0
  6. package/docs/custom_model_controller.md +4 -4
  7. package/docs/data-helpers.md +50 -0
  8. package/docs/data_contract.md +9 -7
  9. package/docs/getting-started.md +75 -0
  10. package/docs/headless-environment.md +68 -0
  11. package/docs/index.md +17 -4
  12. package/docs/rules.md +6 -2
  13. package/docs/system_specs.md +49 -6
  14. package/docs/theming.md +64 -0
  15. package/docs/training.md +20 -14
  16. package/docs/troubleshooting.md +76 -0
  17. package/docs/upgrading-hosts-to-3.md +42 -0
  18. package/examples/python/README.md +6 -0
  19. package/examples/python/base_policy_server.py +19 -0
  20. package/package.json +10 -3
  21. package/src/api/PaddockSimulatorController.js +78 -22
  22. package/src/api/runtimeTheme.js +59 -0
  23. package/src/app/F1SimulatorApp.js +24 -3
  24. package/src/app/readouts/timingTowerRenderer.js +203 -42
  25. package/src/app/runtime/frameLoop.js +5 -1
  26. package/src/config/defaultOptions.js +8 -1
  27. package/src/config/themeOptions.js +5 -0
  28. package/src/data/index.d.ts +212 -0
  29. package/src/data/index.js +14 -0
  30. package/src/environment/episode.js +11 -1
  31. package/src/environment/metrics.js +59 -16
  32. package/src/environment/observationVector.js +50 -33
  33. package/src/environment/observations.js +89 -20
  34. package/src/environment/runtime.js +41 -0
  35. package/src/environment/sensors/carRays.js +48 -37
  36. package/src/environment/sensors/index.js +501 -97
  37. package/src/environment/sensors/indexedRayBands.js +134 -46
  38. package/src/environment/sensors/rayBandTrace.js +1171 -0
  39. package/src/environment/sensors/rayChannels.js +108 -0
  40. package/src/environment/sensors/rayConfig.js +71 -25
  41. package/src/environment/sensors/rayGeometry.js +23 -17
  42. package/src/environment/sensors/sensorTargets.js +67 -1
  43. package/src/environment/sensors/surfaceRays.js +130 -89
  44. package/src/environment/sensors/trackRays.js +115 -1
  45. package/src/index.d.ts +43 -0
  46. package/src/index.js +27 -2
  47. package/src/simulation/collisionGeometry.js +260 -96
  48. package/src/simulation/pit/pitFlow.js +21 -9
  49. package/src/simulation/pit/pitQueue.js +5 -3
  50. package/src/simulation/pit/pitRouteMovement.js +9 -4
  51. package/src/simulation/pit/pitRouting.js +77 -42
  52. package/src/simulation/pit/pitState.js +1 -0
  53. package/src/simulation/pit/pitTireService.js +7 -3
  54. package/src/simulation/race/raceFinish.js +25 -8
  55. package/src/simulation/race/raceOrder.js +189 -32
  56. package/src/simulation/race/raceProgress.js +129 -37
  57. package/src/simulation/race/raceSetup.js +2 -0
  58. package/src/simulation/race/raceStep.js +25 -13
  59. package/src/simulation/raceSimulation.js +20 -17
  60. package/src/simulation/rules/collisionSteward.js +40 -23
  61. package/src/simulation/rules/rulesReview.js +77 -56
  62. package/src/simulation/snapshots/raceSnapshots.js +37 -0
  63. package/src/simulation/timing/drsTiming.js +143 -30
  64. package/src/simulation/timing/gapEstimation.js +25 -3
  65. package/src/simulation/timing/lapTelemetry.js +115 -36
  66. package/src/simulation/timing/raceTiming.js +5 -1
  67. package/src/simulation/timing/sectorPerformance.js +104 -26
  68. package/src/simulation/timing/timingHistory.js +229 -14
  69. package/src/simulation/timing/timingLines.js +65 -7
  70. package/src/simulation/track/pitLaneLayout.js +23 -0
  71. package/src/simulation/track/pitLaneState.js +218 -19
  72. package/src/simulation/track/spatialQueries.js +67 -11
  73. package/src/simulation/track/trackMath.js +91 -24
  74. package/src/simulation/track/trackModel.js +2 -0
  75. package/src/simulation/track/trackQueryIndex.js +1334 -100
  76. package/src/simulation/track/trackQueryPitIndex.js +138 -14
  77. package/src/simulation/track/trackQueryScratch.js +7 -0
  78. package/src/simulation/track/trackQueryStats.js +25 -0
  79. package/src/simulation/track/trackSnapshotJson.js +139 -0
  80. package/src/simulation/track/trackStatePolicy.js +249 -1
  81. package/src/simulation/vehicle/contactResolution.js +96 -59
  82. package/src/simulation/vehicle/mainTrackWheelSurface.js +73 -34
  83. package/src/simulation/vehicle/pitWheelSurface.js +81 -28
  84. package/src/simulation/vehicle/runoffResponse.js +54 -29
  85. package/src/simulation/vehicle/vehicleGeometry.js +245 -51
  86. package/src/simulation/vehicle/vehiclePhysics.js +42 -27
  87. package/src/simulation/vehicle/vehicleSnapshots.js +23 -0
  88. package/src/simulation/vehicle/vehicleState.js +6 -0
  89. package/src/simulation/vehicle/wheelSurface.js +807 -92
  90. package/src/styles.css +497 -497
@@ -195,6 +195,9 @@ export function createEnvironmentRuntime(host) {
195
195
  episodeState.lastObservationSnapshot = null;
196
196
  episodeState.lastRewardSnapshot = null;
197
197
  episodeState.drivers?.clear?.();
198
+ episodeState.destroyedCarsById?.clear?.();
199
+ clearObservationScratch(episodeState.observationScratch);
200
+ clearMetricScratch(episodeState.metricScratch);
198
201
  }
199
202
 
200
203
  return { reset, step, resetDrivers, getObservation, getState, getActionSpec, getObservationSpec, destroy };
@@ -292,6 +295,7 @@ function buildResult({
292
295
  options,
293
296
  events,
294
297
  controlledDrivers: resultDrivers,
298
+ scratch: episodeState.observationScratch,
295
299
  });
296
300
  const episode = evaluateEpisode(observationSnapshot, options, episodeState, resultDrivers);
297
301
  const metrics = buildDriverMetrics({
@@ -299,6 +303,7 @@ function buildResult({
299
303
  previousSnapshot: episodeState.previousSnapshot,
300
304
  options: { ...options, controlledDrivers: resultDrivers },
301
305
  events,
306
+ scratch: episodeState.metricScratch,
302
307
  });
303
308
  const rewardEpisodeInfo = buildDriverEpisodeInfo(episodeState, {
304
309
  ...options,
@@ -345,6 +350,42 @@ function buildResult({
345
350
  };
346
351
  }
347
352
 
353
+ function clearObservationScratch(scratch) {
354
+ if (!scratch) return;
355
+ scratch.carsById?.clear?.();
356
+ scratch.eventsByDriver?.clear?.();
357
+ scratch.controlledEventDrivers?.clear?.();
358
+ clearRayBatchContextScratch(scratch.rayBatchContextScratch);
359
+ if (Array.isArray(scratch.eventDriverIds)) scratch.eventDriverIds.length = 0;
360
+ }
361
+
362
+ function clearRayBatchContextScratch(scratch) {
363
+ if (!scratch) return;
364
+ const arrayKeys = [
365
+ 'rayTargets',
366
+ 'rayTargetPool',
367
+ 'carTargets',
368
+ 'rayVectors',
369
+ 'sharedRayQueries',
370
+ 'rays',
371
+ 'rayPool',
372
+ 'rayVectorValues',
373
+ 'rayVectorValuePool',
374
+ ];
375
+ for (let index = 0; index < arrayKeys.length; index += 1) {
376
+ const value = scratch[arrayKeys[index]];
377
+ if (Array.isArray(value)) value.length = 0;
378
+ }
379
+ }
380
+
381
+ function clearMetricScratch(scratch) {
382
+ if (!scratch) return;
383
+ scratch.previousCarsById?.clear?.();
384
+ scratch.currentCarsById?.clear?.();
385
+ scratch.contactCounts?.clear?.();
386
+ if (Array.isArray(scratch.eventDriverIds)) scratch.eventDriverIds.length = 0;
387
+ }
388
+
348
389
  function applyControlledRunoffResponse(sim, options) {
349
390
  options?.controlledDrivers?.forEach?.((driverId) => {
350
391
  const car = sim?.cars?.find?.((item) => item.id === driverId);
@@ -1,6 +1,6 @@
1
1
  import { metersToSimUnits, simUnitsToMeters } from '../../simulation/units.js';
2
2
  import { VEHICLE_GEOMETRY } from '../../simulation/vehicle/vehicleGeometry.js';
3
- import { dot, getCarRayOrigin, getCarRayVector, intersectAxisAlignedBoxRay } from './rayGeometry.js';
3
+ import { getCarRayOrigin, getCarRayVector, intersectAxisAlignedBoxRayScalars } from './rayGeometry.js';
4
4
  import { rayDetectableTargets } from './sensorTargets.js';
5
5
 
6
6
  export function estimateCarHit(
@@ -10,41 +10,53 @@ export function estimateCarHit(
10
10
  lengthMeters,
11
11
  origin = getCarRayOrigin(car),
12
12
  targets = rayDetectableTargets(car, snapshot),
13
+ options = {},
13
14
  ) {
14
- const ray = getCarRayVector(car, angleDegrees);
15
+ const ray = options.rayVector ?? getCarRayVector(car, angleDegrees);
16
+ if (options.stats) {
17
+ if (options.rayVector) options.stats.callerVectorCount += 1;
18
+ else options.stats.computedVectorCount += 1;
19
+ }
15
20
  const maxDistance = metersToSimUnits(lengthMeters);
16
- let closest = null;
21
+ let closestTarget = null;
22
+ let closestDistance = Infinity;
17
23
  targets.forEach((other) => {
18
24
  if (!carRayBroadphaseHit(origin, ray, maxDistance, other)) return;
19
25
  const hitDistance = intersectCarFootprint(origin, ray, other);
20
26
  if (hitDistance == null || hitDistance > maxDistance) return;
21
- const distanceMeters = simUnitsToMeters(hitDistance);
22
- if (!closest || hitDistance < closest.distanceSimUnits) {
23
- closest = {
24
- hit: true,
25
- distanceSimUnits: hitDistance,
26
- distanceMeters,
27
- driverId: other.id,
28
- targetId: other.id,
29
- targetType: other.entityType,
30
- relativeSpeedKph: other.speedKph - car.speedKph,
31
- };
27
+ if (hitDistance < closestDistance) {
28
+ closestDistance = hitDistance;
29
+ closestTarget = other;
32
30
  }
33
31
  });
34
- if (!closest) return createCarRayMiss(lengthMeters);
35
- const { distanceSimUnits, ...publicHit } = closest;
36
- return publicHit;
32
+ const resultTarget = options.resultTarget ?? null;
33
+ if (resultTarget && options.stats) options.stats.resultTargetCount += 1;
34
+ if (!closestTarget) return writeCarRayMiss(resultTarget ?? {}, lengthMeters);
35
+ return writeCarRayResult(resultTarget ?? {}, car, closestTarget, closestDistance);
37
36
  }
38
37
 
39
38
  export function createCarRayMiss(lengthMeters) {
40
- return {
41
- hit: false,
42
- distanceMeters: lengthMeters,
43
- driverId: null,
44
- targetId: null,
45
- targetType: null,
46
- relativeSpeedKph: 0,
47
- };
39
+ return writeCarRayMiss({}, lengthMeters);
40
+ }
41
+
42
+ function writeCarRayMiss(target, lengthMeters) {
43
+ target.hit = false;
44
+ target.distanceMeters = lengthMeters;
45
+ target.driverId = null;
46
+ target.targetId = null;
47
+ target.targetType = null;
48
+ target.relativeSpeedKph = 0;
49
+ return target;
50
+ }
51
+
52
+ function writeCarRayResult(target, car, other, distanceSimUnits) {
53
+ target.hit = true;
54
+ target.distanceMeters = simUnitsToMeters(distanceSimUnits);
55
+ target.driverId = other.id;
56
+ target.targetId = other.id;
57
+ target.targetType = other.entityType;
58
+ target.relativeSpeedKph = other.speedKph - car.speedKph;
59
+ return target;
48
60
  }
49
61
 
50
62
  function carRayBroadphaseHit(origin, ray, maxDistance, other) {
@@ -58,18 +70,17 @@ function carRayBroadphaseHit(origin, ray, maxDistance, other) {
58
70
  }
59
71
 
60
72
  function intersectCarFootprint(origin, ray, other) {
61
- const forward = { x: Math.cos(other.heading), y: Math.sin(other.heading) };
62
- const right = { x: -Math.sin(other.heading), y: Math.cos(other.heading) };
63
- const delta = { x: origin.x - other.x, y: origin.y - other.y };
64
- const localOrigin = {
65
- x: dot(delta, forward),
66
- y: dot(delta, right),
67
- };
68
- const localRay = {
69
- x: dot(ray, forward),
70
- y: dot(ray, right),
71
- };
73
+ const forwardX = Math.cos(other.heading);
74
+ const forwardY = Math.sin(other.heading);
75
+ const rightX = -forwardY;
76
+ const rightY = forwardX;
77
+ const deltaX = origin.x - other.x;
78
+ const deltaY = origin.y - other.y;
79
+ const localOriginX = deltaX * forwardX + deltaY * forwardY;
80
+ const localOriginY = deltaX * rightX + deltaY * rightY;
81
+ const localRayX = ray.x * forwardX + ray.y * forwardY;
82
+ const localRayY = ray.x * rightX + ray.y * rightY;
72
83
  const halfLength = VEHICLE_GEOMETRY.bodyLength / 2;
73
84
  const halfWidth = VEHICLE_GEOMETRY.bodyWidth / 2;
74
- return intersectAxisAlignedBoxRay(localOrigin, localRay, halfLength, halfWidth);
85
+ return intersectAxisAlignedBoxRayScalars(localOriginX, localOriginY, localRayX, localRayY, halfLength, halfWidth);
75
86
  }