@inventure71/paddockjs 2.0.1 → 3.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 (80) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/INSTALL_AND_UPDATE.md +10 -1
  3. package/README.md +65 -15
  4. package/docs/architecture.md +17 -13
  5. package/docs/component_inventory.md +72 -0
  6. package/docs/concepts.md +2 -2
  7. package/docs/custom_model_controller.md +2 -2
  8. package/docs/data_contract.md +96 -21
  9. package/docs/index.md +2 -1
  10. package/docs/learnings.md +6 -6
  11. package/docs/rules.md +7 -7
  12. package/docs/sense_contract.md +1 -1
  13. package/docs/system_specs.md +25 -18
  14. package/docs/training.md +13 -12
  15. package/package.json +1 -1
  16. package/src/api/PaddockSimulatorController.js +74 -4
  17. package/src/app/F1SimulatorApp.js +191 -28
  18. package/src/app/banners/raceDataBannerController.js +71 -36
  19. package/src/app/banners/stewardMessageController.js +5 -4
  20. package/src/app/camera/cameraConstants.js +2 -0
  21. package/src/app/camera/cameraController.js +86 -29
  22. package/src/app/camera/cameraGeometry.js +12 -1
  23. package/src/app/domBindings.js +89 -9
  24. package/src/app/layoutScheduler.js +27 -0
  25. package/src/app/layoutSupport.js +165 -0
  26. package/src/app/raceOverlayClearanceSupport.js +158 -0
  27. package/src/app/readouts/carOverviewRenderer.js +16 -8
  28. package/src/app/readouts/controlStateRenderer.js +1 -1
  29. package/src/app/readouts/raceStatusRenderer.js +30 -21
  30. package/src/app/readouts/readoutFormatters.js +2 -0
  31. package/src/app/readouts/telemetryRenderer.js +17 -8
  32. package/src/app/readouts/timingTowerRenderer.js +62 -10
  33. package/src/app/runtime/runtimeVisibility.js +66 -5
  34. package/src/config/cssValues.js +16 -0
  35. package/src/config/defaultOptions.js +51 -38
  36. package/src/config/themeOptions.js +825 -0
  37. package/src/config/timingGapMode.js +13 -0
  38. package/src/config/urlOptions.js +25 -0
  39. package/src/data/championship.js +14 -9
  40. package/src/environment/controllerLoop.js +17 -3
  41. package/src/environment/index.d.ts +1 -2
  42. package/src/environment/options.js +1 -1
  43. package/src/environment/runtime.js +3 -11
  44. package/src/environment/scenarios.js +1 -1
  45. package/src/environment/sensors/surfaceRays.js +41 -33
  46. package/src/environment/sensors/trackRays.js +44 -49
  47. package/src/index.d.ts +110 -9
  48. package/src/index.js +25 -1
  49. package/src/simulation/driver/driverController.js +1 -1
  50. package/src/simulation/driver/edgeRecovery.js +1 -1
  51. package/src/simulation/driver/racingControls.js +2 -2
  52. package/src/simulation/driver/rejoinControls.js +2 -2
  53. package/src/simulation/pit/pitKinematics.js +2 -2
  54. package/src/simulation/pit/pitRouting.js +2 -4
  55. package/src/simulation/race/raceSetup.js +2 -6
  56. package/src/simulation/race/raceStep.js +1 -1
  57. package/src/simulation/race/safetyCar.js +1 -1
  58. package/src/simulation/race/stalledDnf.js +1 -1
  59. package/src/simulation/timing/lapTelemetry.js +28 -9
  60. package/src/simulation/track/pitLaneLayout.js +7 -3
  61. package/src/simulation/track/spatialQueries.js +5 -28
  62. package/src/simulation/track/trackQueryIndex.js +180 -135
  63. package/src/simulation/track/trackStatePolicy.js +0 -1
  64. package/src/simulation/vehicle/contactResolution.js +3 -3
  65. package/src/simulation/vehicle/vehicleKinematics.js +2 -2
  66. package/src/simulation/vehicle/vehiclePhysics.js +3 -3
  67. package/src/simulation/vehicle/vehicleSnapshots.js +12 -1
  68. package/src/simulation/vehicle/vehicleState.js +1 -1
  69. package/src/simulation/warmup/runtimeWarmup.js +0 -1
  70. package/src/styles.css +1185 -136
  71. package/src/ui/bannerTemplates.js +38 -28
  72. package/src/ui/cameraControlsTemplate.js +16 -5
  73. package/src/ui/carOverviewTemplate.js +12 -5
  74. package/src/ui/raceCanvasTemplate.js +31 -8
  75. package/src/ui/raceControlsTemplate.js +27 -10
  76. package/src/ui/shellTemplate.js +3 -1
  77. package/src/ui/telemetryDrawerTemplate.js +23 -10
  78. package/src/ui/telemetryTemplates.js +58 -32
  79. package/src/ui/templateUtils.js +48 -0
  80. package/src/ui/timingTowerTemplate.js +29 -12
@@ -3,11 +3,18 @@ import { CHAMPIONSHIP_ENTRY_BLUEPRINTS } from '../data/championship.js';
3
3
  import { normalizeSimulatorDrivers } from '../data/normalizeDrivers.js';
4
4
  import { normalizePhysicsMode } from '../simulation/vehicle/vehiclePhysics.js';
5
5
  import { normalizeWarmupOptions } from '../simulation/warmup/runtimeWarmup.js';
6
+ import {
7
+ DEFAULT_PADDOCK_THEME_INPUT,
8
+ applyPaddockThemeCssVariables,
9
+ mergeThemeInputs,
10
+ normalizePaddockTheme,
11
+ } from './themeOptions.js';
12
+ import { normalizeTimingGapMode } from './timingGapMode.js';
13
+ import { normalizePublicUrlOption } from './urlOptions.js';
6
14
 
7
15
  export const DEFAULT_F1_SIMULATOR_OPTIONS = {
8
16
  seed: 1971,
9
17
  physicsMode: 'arcade',
10
- trackQueryIndex: true,
11
18
  warmup: {
12
19
  enabled: true,
13
20
  policy: 'config-change',
@@ -34,23 +41,22 @@ export const DEFAULT_F1_SIMULATOR_OPTIONS = {
34
41
  },
35
42
  showRaceDataPanel: true,
36
43
  raceDataTelemetryDetail: false,
44
+ driverCamera: false,
37
45
  raceDataBanners: {
38
46
  initial: 'project',
39
47
  enabled: ['project', 'radio'],
40
48
  },
41
49
  raceDataBannerSize: 'custom',
42
50
  timingTowerVerticalFit: 'expand-race-view',
51
+ timingGapMode: 'interval',
52
+ timingGapModeToggle: true,
53
+ timingEntryVerticalPadding: 5,
54
+ responsiveNarrowLayout: true,
43
55
  },
44
56
  debug: {
45
57
  physicsModeIndicator: false,
46
58
  },
47
- theme: {
48
- accentColor: '#e10600',
49
- greenColor: '#14c784',
50
- yellowColor: '#ffd166',
51
- timingTowerMaxWidth: '390px',
52
- raceViewMinHeight: '620px',
53
- },
59
+ theme: DEFAULT_PADDOCK_THEME_INPUT,
54
60
  };
55
61
 
56
62
  export const PADDOCK_SIMULATOR_PRESETS = {
@@ -113,15 +119,14 @@ export const PADDOCK_SIMULATOR_PRESETS = {
113
119
  },
114
120
  };
115
121
 
116
- export const PADDOCK_THEME_CSS_VARIABLES = {
117
- accentColor: '--paddock-accent-color',
118
- greenColor: '--paddock-green-color',
119
- yellowColor: '--paddock-yellow-color',
120
- timingTowerMaxWidth: '--paddock-timing-tower-max-width',
121
- raceViewMinHeight: '--paddock-race-view-min-height',
122
- };
122
+ export { PADDOCK_THEME_CSS_VARIABLES, applyPaddockThemeCssVariables } from './themeOptions.js';
123
123
 
124
- const SUPPORTED_CAMERA_MODES = new Set(['overview', 'leader', 'selected', 'show-all', 'pit']);
124
+ const SUPPORTED_CAMERA_MODES = new Set(['overview', 'leader', 'selected', 'driver', 'show-all', 'pit']);
125
+
126
+ function normalizeNonNegativeFiniteNumber(value, fallback) {
127
+ const numericValue = Number(value);
128
+ return Number.isFinite(numericValue) && numericValue >= 0 ? numericValue : fallback;
129
+ }
125
130
 
126
131
  export function resolveF1SimulatorOptions(options = {}) {
127
132
  const presetName = Object.hasOwn(PADDOCK_SIMULATOR_PRESETS, options.preset)
@@ -130,6 +135,9 @@ export function resolveF1SimulatorOptions(options = {}) {
130
135
  const preset = presetName ? PADDOCK_SIMULATOR_PRESETS[presetName] : {};
131
136
  const requestedUi = options.ui ?? {};
132
137
  const presetUi = preset.ui ?? {};
138
+ const initialCameraMode = SUPPORTED_CAMERA_MODES.has(options.initialCameraMode)
139
+ ? options.initialCameraMode
140
+ : DEFAULT_F1_SIMULATOR_OPTIONS.initialCameraMode;
133
141
  const ui = {
134
142
  ...DEFAULT_F1_SIMULATOR_OPTIONS.ui,
135
143
  ...presetUi,
@@ -140,6 +148,7 @@ export function resolveF1SimulatorOptions(options = {}) {
140
148
  ...(requestedUi.raceDataBanners ?? {}),
141
149
  },
142
150
  };
151
+ ui.driverCamera = Boolean(ui.driverCamera || initialCameraMode === 'driver');
143
152
  ui.raceDataBanners.enabled = normalizeEnabledBanners(ui.raceDataBanners.enabled);
144
153
  ui.telemetryModules = normalizeTelemetryModules(ui.telemetryModules);
145
154
  if (!['project', 'radio', 'hidden'].includes(ui.raceDataBanners.initial)) {
@@ -151,6 +160,13 @@ export function resolveF1SimulatorOptions(options = {}) {
151
160
  if (!['expand-race-view', 'scroll'].includes(ui.timingTowerVerticalFit)) {
152
161
  ui.timingTowerVerticalFit = DEFAULT_F1_SIMULATOR_OPTIONS.ui.timingTowerVerticalFit;
153
162
  }
163
+ ui.timingGapMode = normalizeTimingGapMode(ui.timingGapMode);
164
+ ui.timingGapModeToggle = ui.timingGapModeToggle !== false;
165
+ ui.timingEntryVerticalPadding = normalizeNonNegativeFiniteNumber(
166
+ ui.timingEntryVerticalPadding,
167
+ DEFAULT_F1_SIMULATOR_OPTIONS.ui.timingEntryVerticalPadding,
168
+ );
169
+ ui.responsiveNarrowLayout = ui.responsiveNarrowLayout !== false;
154
170
  if (!['auto', 'custom'].includes(ui.raceDataBannerSize)) {
155
171
  ui.raceDataBannerSize = DEFAULT_F1_SIMULATOR_OPTIONS.ui.raceDataBannerSize;
156
172
  }
@@ -161,28 +177,30 @@ export function resolveF1SimulatorOptions(options = {}) {
161
177
  ...(options.debug ?? {}),
162
178
  };
163
179
  debug.physicsModeIndicator = Boolean(debug.physicsModeIndicator);
164
- const initialCameraMode = SUPPORTED_CAMERA_MODES.has(options.initialCameraMode)
165
- ? options.initialCameraMode
166
- : DEFAULT_F1_SIMULATOR_OPTIONS.initialCameraMode;
167
180
  const mergedOptions = {
168
181
  ...DEFAULT_F1_SIMULATOR_OPTIONS,
169
182
  ...preset,
170
183
  ...options,
171
184
  };
172
- const theme = normalizeTheme({
173
- ...DEFAULT_F1_SIMULATOR_OPTIONS.theme,
174
- ...(preset.theme ?? {}),
175
- ...(options.theme ?? {}),
176
- });
177
185
  const drivers = normalizeSimulatorDrivers(mergedOptions.drivers, {
178
186
  entries: mergedOptions.entries ?? CHAMPIONSHIP_ENTRY_BLUEPRINTS,
179
187
  });
188
+ const theme = normalizePaddockTheme(mergeThemeInputs(
189
+ DEFAULT_F1_SIMULATOR_OPTIONS.theme,
190
+ preset.theme,
191
+ collectDriverTeamThemes(drivers),
192
+ options.theme,
193
+ ));
180
194
 
181
195
  return {
182
196
  ...DEFAULT_F1_SIMULATOR_OPTIONS,
183
197
  ...preset,
184
198
  ...options,
185
199
  preset: presetName ?? options.preset,
200
+ backLinkHref: normalizePublicUrlOption(
201
+ mergedOptions.backLinkHref,
202
+ DEFAULT_F1_SIMULATOR_OPTIONS.backLinkHref,
203
+ ),
186
204
  physicsMode: normalizePhysicsMode(options.physicsMode ?? preset.physicsMode),
187
205
  warmup: normalizeWarmupOptions(mergedOptions.warmup, 'browser'),
188
206
  initialCameraMode,
@@ -194,12 +212,16 @@ export function resolveF1SimulatorOptions(options = {}) {
194
212
  };
195
213
  }
196
214
 
197
- export function applyPaddockThemeCssVariables(root, theme = DEFAULT_F1_SIMULATOR_OPTIONS.theme) {
198
- Object.entries(PADDOCK_THEME_CSS_VARIABLES).forEach(([key, variable]) => {
199
- const value = theme?.[key];
200
- if (value == null || value === '') return;
201
- root?.style?.setProperty?.(variable, String(value));
215
+ function collectDriverTeamThemes(drivers = []) {
216
+ const teamThemes = {};
217
+ drivers.forEach((driver) => {
218
+ const teamId = driver?.team?.id;
219
+ const theme = driver?.team?.theme;
220
+ if (typeof teamId === 'string' && teamId !== '' && typeof theme === 'string' && theme !== '') {
221
+ teamThemes[teamId] = theme;
222
+ }
202
223
  });
224
+ return Object.keys(teamThemes).length > 0 ? { teamThemes } : {};
203
225
  }
204
226
 
205
227
  function normalizeEnabledBanners(value) {
@@ -226,12 +248,3 @@ function normalizeTelemetryModules(value) {
226
248
  value[name] == null ? defaults[name] : Boolean(value[name]),
227
249
  ]));
228
250
  }
229
-
230
- function normalizeTheme(theme) {
231
- return Object.fromEntries(
232
- Object.entries(DEFAULT_F1_SIMULATOR_OPTIONS.theme).map(([key, fallback]) => [
233
- key,
234
- theme?.[key] == null || theme?.[key] === '' ? fallback : theme[key],
235
- ]),
236
- );
237
- }