@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
@@ -127,6 +127,16 @@ export function expandBounds(bounds, point) {
127
127
  };
128
128
  }
129
129
 
130
+ export function expandBoundsByPadding(bounds, padding = 0) {
131
+ const resolvedPadding = Number.isFinite(padding) ? Math.max(0, padding) : 0;
132
+ return {
133
+ minX: bounds.minX - resolvedPadding,
134
+ maxX: bounds.maxX + resolvedPadding,
135
+ minY: bounds.minY - resolvedPadding,
136
+ maxY: bounds.maxY + resolvedPadding,
137
+ };
138
+ }
139
+
130
140
  export function createPointBounds(points, padding = 0) {
131
141
  const bounds = points.filter(Boolean).reduce(
132
142
  (current, point) => expandBounds(current, point),
@@ -181,43 +191,100 @@ export function projectPointToSegment(position, start, end) {
181
191
  }
182
192
 
183
193
  export function nearestPointOnPolyline(points, position, segmentIndices = null) {
194
+ return nearestPointOnPolylineInto({}, points, position, { segmentIndices });
195
+ }
196
+
197
+ export function nearestPointOnPolylineInto(target, points, position, {
198
+ segmentIndices = null,
199
+ scratch = null,
200
+ cumulativeDistances: precomputedCumulativeDistances = null,
201
+ } = {}) {
184
202
  if (!Array.isArray(points) || points.length < 2) return null;
185
- const restrictedSegments = Array.isArray(segmentIndices) && segmentIndices.length > 0
186
- ? [...new Set(segmentIndices)].filter((index) => Number.isInteger(index) && index >= 0 && index < points.length - 1)
203
+ const output = target ?? {};
204
+ const outputPoint = output.point ?? {};
205
+ output.point = outputPoint;
206
+ const restrictedSegments = Array.isArray(segmentIndices) && segmentIndices.length > 0;
207
+ const cumulativeDistances = restrictedSegments
208
+ ? (Array.isArray(precomputedCumulativeDistances) && precomputedCumulativeDistances.length >= points.length
209
+ ? precomputedCumulativeDistances
210
+ : writePolylineCumulativeDistances(scratch?.cumulativeDistances ?? new Array(points.length), points))
187
211
  : null;
188
- const cumulativeDistances = restrictedSegments ? createPolylineCumulativeDistances(points) : null;
189
- let best = null;
212
+ if (scratch && restrictedSegments && cumulativeDistances !== precomputedCumulativeDistances) {
213
+ scratch.cumulativeDistances = cumulativeDistances;
214
+ }
215
+
216
+ let bestSegmentIndex = -1;
217
+ let bestAmount = 0;
218
+ let bestLength = 0;
219
+ let bestHeading = 0;
220
+ let bestNormalX = 0;
221
+ let bestNormalY = 1;
222
+ let bestSignedOffset = 0;
223
+ let bestCrossTrackError = Infinity;
224
+ let bestDistanceAlong = 0;
225
+ let bestX = 0;
226
+ let bestY = 0;
190
227
  let distanceBefore = 0;
191
228
  let totalLength = 0;
192
229
 
193
- const segmentCount = restrictedSegments?.length ?? (points.length - 1);
230
+ const segmentCount = restrictedSegments ? segmentIndices.length : (points.length - 1);
194
231
  for (let segmentOffset = 0; segmentOffset < segmentCount; segmentOffset += 1) {
195
- const index = restrictedSegments ? restrictedSegments[segmentOffset] : segmentOffset;
232
+ const index = restrictedSegments ? segmentIndices[segmentOffset] : segmentOffset;
233
+ if (!Number.isInteger(index) || index < 0 || index >= points.length - 1) continue;
196
234
  const current = points[index];
197
235
  const next = points[index + 1];
198
- const projection = projectPointToSegment(position, current, next);
199
- if (!best || projection.crossTrackError < best.crossTrackError) {
200
- best = {
201
- ...projection,
202
- segmentIndex: index,
203
- distanceAlong: (restrictedSegments ? cumulativeDistances[index] : distanceBefore) + projection.length * projection.amount,
204
- };
236
+ const dx = next.x - current.x;
237
+ const dy = next.y - current.y;
238
+ const lengthSquared = dx * dx + dy * dy;
239
+ const amount = lengthSquared > 0
240
+ ? clamp(((position.x - current.x) * dx + (position.y - current.y) * dy) / lengthSquared, 0, 1)
241
+ : 0;
242
+ const projectedX = current.x + dx * amount;
243
+ const projectedY = current.y + dy * amount;
244
+ const length = Math.sqrt(lengthSquared);
245
+ const heading = Math.atan2(dy, dx);
246
+ const normalX = length > 0 ? -dy / length : 0;
247
+ const normalY = length > 0 ? dx / length : 1;
248
+ const signedOffset = (position.x - projectedX) * normalX + (position.y - projectedY) * normalY;
249
+ const crossTrackError = Math.abs(signedOffset);
250
+ if (crossTrackError < bestCrossTrackError) {
251
+ bestSegmentIndex = index;
252
+ bestAmount = amount;
253
+ bestLength = length;
254
+ bestHeading = heading;
255
+ bestNormalX = normalX;
256
+ bestNormalY = normalY;
257
+ bestSignedOffset = signedOffset;
258
+ bestCrossTrackError = crossTrackError;
259
+ bestDistanceAlong = (restrictedSegments ? cumulativeDistances[index] : distanceBefore) + length * amount;
260
+ bestX = projectedX;
261
+ bestY = projectedY;
205
262
  }
206
- if (!restrictedSegments) distanceBefore += projection.length;
207
- totalLength += projection.length;
263
+ if (!restrictedSegments) distanceBefore += length;
264
+ totalLength += length;
208
265
  }
209
266
 
210
- if (!best) return null;
211
- return {
212
- ...best,
213
- totalLength: restrictedSegments
214
- ? cumulativeDistances[cumulativeDistances.length - 1]
215
- : totalLength,
216
- };
267
+ if (bestSegmentIndex < 0) return null;
268
+ outputPoint.x = bestX;
269
+ outputPoint.y = bestY;
270
+ output.amount = bestAmount;
271
+ output.length = bestLength;
272
+ output.heading = bestHeading;
273
+ output.normalX = bestNormalX;
274
+ output.normalY = bestNormalY;
275
+ output.signedOffset = bestSignedOffset;
276
+ output.crossTrackError = bestCrossTrackError;
277
+ output.segmentIndex = bestSegmentIndex;
278
+ output.distanceAlong = bestDistanceAlong;
279
+ output.totalLength = restrictedSegments
280
+ ? cumulativeDistances[cumulativeDistances.length - 1]
281
+ : totalLength;
282
+ return output;
217
283
  }
218
284
 
219
- function createPolylineCumulativeDistances(points) {
220
- const distances = new Array(points.length).fill(0);
285
+ function writePolylineCumulativeDistances(distances, points) {
286
+ distances.length = points.length;
287
+ distances[0] = 0;
221
288
  for (let index = 1; index < points.length; index += 1) {
222
289
  distances[index] = distances[index - 1] + distance(points[index - 1], points[index]);
223
290
  }
@@ -11,6 +11,7 @@ import { deriveDrsZones, normalizeDrsZone } from './drsZones.js';
11
11
  import { createPitLaneModel } from './pitLaneLayout.js';
12
12
  import { attachTrackQueryIndex, createTrackQueryIndex } from './trackQueryIndex.js';
13
13
  import { resolveProceduralTrackOptions } from './trackGenerationOptions.js';
14
+ import { attachTrackSnapshotJsonSerializer } from './trackSnapshotJson.js';
14
15
 
15
16
  export { WORLD, TRACK } from './trackConstants.js';
16
17
  export { isInDrsZone } from './drsZones.js';
@@ -56,6 +57,7 @@ export function buildTrackModel(track = TRACK) {
56
57
  ? null
57
58
  : createPitLaneModel(model);
58
59
  attachTrackQueryIndex(model, createTrackQueryIndex(model));
60
+ attachTrackSnapshotJsonSerializer(model);
59
61
  if (canReuseCachedModel) {
60
62
  TRACK_MODEL_CACHE.set(track, freezeTrackModel(model));
61
63
  }