@inventure71/paddockjs 0.3.0 → 2.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.
- package/CHANGELOG.md +91 -0
- package/INSTALL_AND_UPDATE.md +5 -2
- package/README.md +328 -13
- package/docs/architecture.md +150 -41
- package/docs/concepts.md +22 -3
- package/docs/custom_model_controller.md +329 -0
- package/docs/data_contract.md +474 -34
- package/docs/data_model.md +1 -1
- package/docs/index.md +1 -0
- package/docs/learnings.md +23 -1
- package/docs/rules.md +169 -21
- package/docs/system_specs.md +56 -20
- package/docs/training.md +369 -25
- package/examples/python/README.md +62 -0
- package/examples/python/base_policy_server.py +191 -0
- package/examples/python/requirements.txt +2 -0
- package/examples/train-basic-policy.mjs +5 -6
- package/examples/trainingData.mjs +133 -0
- package/package.json +15 -4
- package/src/api/PaddockSimulatorController.js +36 -2
- package/src/app/BrowserExpertAdapter.js +367 -9
- package/src/app/F1SimulatorApp.js +615 -940
- package/src/app/banners/bannerFormatters.js +55 -0
- package/src/app/banners/raceDataBannerController.js +141 -0
- package/src/app/banners/stewardMessageController.js +99 -0
- package/src/app/camera/cameraConstants.js +21 -0
- package/src/app/camera/cameraController.js +265 -0
- package/src/app/camera/cameraGeometry.js +176 -0
- package/src/app/domBindings.js +14 -1
- package/src/app/readouts/carOverviewRenderer.js +109 -0
- package/src/app/readouts/controlStateRenderer.js +37 -0
- package/src/app/readouts/raceStatusRenderer.js +133 -0
- package/src/app/readouts/readoutFormatters.js +58 -0
- package/src/app/readouts/telemetryRenderer.js +113 -0
- package/src/app/readouts/timingTowerRenderer.js +177 -0
- package/src/app/rendering/appAssets.js +38 -0
- package/src/app/rendering/carRenderer.js +286 -0
- package/src/app/rendering/displayUtils.js +22 -0
- package/src/app/rendering/drsTrailRenderer.js +90 -0
- package/src/app/rendering/expertSensorRenderer.js +126 -0
- package/src/app/rendering/pitLaneStatusRenderer.js +50 -0
- package/src/app/rendering/replayGhostRenderer.js +124 -0
- package/src/app/rendering/trackRenderer.js +25 -0
- package/src/app/runtime/frameLoop.js +66 -0
- package/src/app/runtime/runtimeTiming.js +34 -0
- package/src/app/runtime/runtimeVisibility.js +45 -0
- package/src/config/defaultOptions.js +24 -4
- package/src/environment/actions.js +38 -7
- package/src/environment/controllerLoop.js +246 -0
- package/src/environment/episode.js +86 -2
- package/src/environment/evaluation.js +206 -0
- package/src/environment/index.d.ts +638 -6
- package/src/environment/index.js +14 -0
- package/src/environment/metrics.js +159 -0
- package/src/environment/observationOptions.js +33 -0
- package/src/environment/observations.js +500 -43
- package/src/environment/options.js +41 -2
- package/src/environment/recorder.js +28 -0
- package/src/environment/runtime.js +260 -24
- package/src/environment/scenarios.js +249 -0
- package/src/environment/sensors/bodySenses.js +23 -0
- package/src/environment/sensors/boundarySenses.js +17 -0
- package/src/environment/sensors/carRays.js +75 -0
- package/src/environment/sensors/contactSenses.js +32 -0
- package/src/environment/sensors/index.js +157 -0
- package/src/environment/sensors/indexedRayBands.js +103 -0
- package/src/environment/sensors/nearbyCars.js +52 -0
- package/src/environment/sensors/opponentRadar.js +65 -0
- package/src/environment/sensors/rayConfig.js +110 -0
- package/src/environment/sensors/rayDefaults.js +6 -0
- package/src/environment/sensors/rayGeometry.js +60 -0
- package/src/environment/sensors/rayGuards.js +19 -0
- package/src/environment/sensors/sensorTargets.js +60 -0
- package/src/environment/sensors/surfaceRays.js +296 -0
- package/src/environment/sensors/trackRays.js +261 -0
- package/src/environment/sensors.js +1 -257
- package/src/environment/specs.js +188 -9
- package/src/environment/workerProtocol.js +64 -0
- package/src/index.d.ts +533 -7
- package/src/index.js +26 -0
- package/src/rendering/proceduralTrackAsset.js +20 -510
- package/src/rendering/renderSnapshot.js +38 -12
- package/src/rendering/track/finishLineRenderer.js +34 -0
- package/src/rendering/track/gridRenderer.js +47 -0
- package/src/rendering/track/offsetStrokeSafety.js +136 -0
- package/src/rendering/track/pitLaneRenderer.js +248 -0
- package/src/rendering/track/trackMaterialRenderer.js +326 -0
- package/src/rendering/track/trackRenderConstants.js +38 -0
- package/src/rendering/track/trackRenderGeometry.js +94 -0
- package/src/simulation/collisionGeometry.js +214 -0
- package/src/simulation/driver/driverControlConstants.js +28 -0
- package/src/simulation/driver/driverController.js +47 -0
- package/src/simulation/driver/driverInput.js +28 -0
- package/src/simulation/driver/driverMath.js +6 -0
- package/src/simulation/driver/driverPersonality.js +25 -0
- package/src/simulation/driver/edgeRecovery.js +48 -0
- package/src/simulation/driver/racingControls.js +399 -0
- package/src/simulation/driver/racingLinePlan.js +166 -0
- package/src/simulation/driver/recoveryDynamics.js +38 -0
- package/src/simulation/driver/rejoinControls.js +213 -0
- package/src/simulation/driver/safetyCarControls.js +35 -0
- package/src/simulation/driver/trafficScan.js +51 -0
- package/src/simulation/driverController.js +4 -253
- package/src/simulation/participants/participantInteractions.js +120 -0
- package/src/simulation/pit/pitFlow.js +234 -0
- package/src/simulation/pit/pitIntent.js +92 -0
- package/src/simulation/pit/pitOccupancy.js +47 -0
- package/src/simulation/pit/pitPenaltyService.js +71 -0
- package/src/simulation/pit/pitQueue.js +55 -0
- package/src/simulation/pit/pitRouteMovement.js +160 -0
- package/src/simulation/pit/pitRouting.js +319 -0
- package/src/simulation/pit/pitService.js +41 -0
- package/src/simulation/pit/pitServiceConstants.js +21 -0
- package/src/simulation/pit/pitServiceProfile.js +45 -0
- package/src/simulation/pit/pitSnapshots.js +61 -0
- package/src/simulation/pit/pitState.js +113 -0
- package/src/simulation/pit/pitTireService.js +92 -0
- package/src/simulation/race/classification.js +104 -0
- package/src/simulation/race/gridPenalties.js +37 -0
- package/src/simulation/race/raceControlState.js +38 -0
- package/src/simulation/race/raceDistance.js +32 -0
- package/src/simulation/race/raceFinish.js +65 -0
- package/src/simulation/race/raceLifecycle.js +71 -0
- package/src/simulation/race/raceOrder.js +113 -0
- package/src/simulation/race/raceProgress.js +124 -0
- package/src/simulation/race/raceSetup.js +93 -0
- package/src/simulation/race/raceStep.js +80 -0
- package/src/simulation/race/redFlag.js +15 -0
- package/src/simulation/race/retirements.js +44 -0
- package/src/simulation/race/safetyCar.js +86 -0
- package/src/simulation/race/stalledDnf.js +86 -0
- package/src/simulation/raceSimulation.js +386 -1012
- package/src/simulation/replay/replayGhosts.js +128 -0
- package/src/simulation/rules/collisionSteward.js +69 -0
- package/src/simulation/rules/moduleConfig.js +72 -0
- package/src/simulation/rules/penaltyConfig.js +99 -0
- package/src/simulation/rules/penaltyEvents.js +18 -0
- package/src/simulation/rules/penaltyLedger.js +153 -0
- package/src/simulation/rules/penaltyStats.js +26 -0
- package/src/simulation/rules/pitLaneSpeedingSteward.js +71 -0
- package/src/simulation/rules/ruleConfigMerge.js +41 -0
- package/src/simulation/rules/ruleDefaults.js +85 -0
- package/src/simulation/rules/rulesReview.js +141 -0
- package/src/simulation/rules/rulesetPresets.js +47 -0
- package/src/simulation/rules/tireRequirementSteward.js +29 -0
- package/src/simulation/rules/trackLimitsSteward.js +85 -0
- package/src/simulation/rulesConfig.js +34 -0
- package/src/simulation/snapshots/raceSnapshots.js +192 -0
- package/src/simulation/timing/drsTiming.js +87 -0
- package/src/simulation/timing/gapEstimation.js +38 -0
- package/src/simulation/timing/lapTelemetry.js +182 -0
- package/src/simulation/timing/raceTiming.js +15 -0
- package/src/simulation/timing/sectorPerformance.js +60 -0
- package/src/simulation/timing/timingConstants.js +5 -0
- package/src/simulation/timing/timingHistory.js +46 -0
- package/src/simulation/timing/timingLines.js +79 -0
- package/src/simulation/track/centerlineSpline.js +59 -0
- package/src/simulation/track/drsZones.js +67 -0
- package/src/simulation/track/pitLaneAccess.js +103 -0
- package/src/simulation/track/pitLaneLayout.js +395 -0
- package/src/simulation/track/pitLaneState.js +160 -0
- package/src/simulation/track/proceduralCenterline.js +237 -0
- package/src/simulation/track/sampleGeometry.js +49 -0
- package/src/simulation/track/spatialQueries.js +154 -0
- package/src/simulation/track/startStraight.js +141 -0
- package/src/simulation/track/trackConstants.js +109 -0
- package/src/simulation/track/trackGenerationOptions.js +283 -0
- package/src/simulation/track/trackMath.js +241 -0
- package/src/simulation/track/trackModel.js +240 -0
- package/src/simulation/track/trackQueryIndex.js +945 -0
- package/src/simulation/track/trackSectors.js +19 -0
- package/src/simulation/track/trackStatePolicy.js +24 -0
- package/src/simulation/track/trackValidation.js +98 -0
- package/src/simulation/trackModel.js +1 -628
- package/src/simulation/units.js +6 -4
- package/src/simulation/vehicle/contactResolution.js +116 -0
- package/src/simulation/vehicle/mainTrackWheelSurface.js +82 -0
- package/src/simulation/vehicle/pitWheelSurface.js +69 -0
- package/src/simulation/vehicle/runoffResponse.js +88 -0
- package/src/simulation/vehicle/surfacePriority.js +28 -0
- package/src/simulation/vehicle/vehicleGeometry.js +247 -0
- package/src/simulation/vehicle/vehiclePhysics.js +363 -0
- package/src/simulation/vehicle/vehicleSnapshots.js +316 -0
- package/src/simulation/vehicle/vehicleState.js +186 -0
- package/src/simulation/vehicle/wheelSurface.js +150 -0
- package/src/simulation/vehicleGeometry.js +1 -0
- package/src/simulation/vehiclePhysics.js +1 -104
- package/src/simulation/warmup/runtimeWarmup.js +224 -0
- package/src/simulation/wheelSurface.js +1 -0
- package/src/styles.css +342 -42
- package/src/ui/bannerTemplates.js +69 -0
- package/src/ui/cameraControlsTemplate.js +20 -0
- package/src/ui/carOverviewTemplate.js +33 -0
- package/src/ui/componentTemplates.js +20 -426
- package/src/ui/raceCanvasTemplate.js +69 -0
- package/src/ui/raceControlStatusBanner.js +39 -0
- package/src/ui/raceControlsTemplate.js +36 -0
- package/src/ui/shellTemplate.js +6 -5
- package/src/ui/telemetryDrawerTemplate.js +55 -0
- package/src/ui/telemetryTemplates.js +136 -0
- package/src/ui/templateUtils.js +23 -0
- package/src/ui/timingTowerTemplate.js +34 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { escapeHtml } from './templateUtils.js';
|
|
2
|
+
|
|
3
|
+
export const RACE_CONTROL_STATUS_BANNERS = {
|
|
4
|
+
'safety-car': {
|
|
5
|
+
status: 'safety-car',
|
|
6
|
+
className: 'is-safety-car',
|
|
7
|
+
kicker: 'FIA',
|
|
8
|
+
title: 'Safety Car',
|
|
9
|
+
},
|
|
10
|
+
'red-flag': {
|
|
11
|
+
status: 'red-flag',
|
|
12
|
+
className: 'is-red-flag',
|
|
13
|
+
kicker: 'FIA',
|
|
14
|
+
title: 'Red Flag',
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function getRaceControlStatusBanner(mode) {
|
|
19
|
+
return RACE_CONTROL_STATUS_BANNERS[mode] ?? null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function createRaceControlStatusBannerMarkup({ status = null } = {}) {
|
|
23
|
+
const banner = getRaceControlStatusBanner(status);
|
|
24
|
+
const className = [
|
|
25
|
+
'broadcast-race-control-banner',
|
|
26
|
+
banner?.className,
|
|
27
|
+
].filter(Boolean).join(' ');
|
|
28
|
+
const hiddenAttribute = banner ? '' : ' hidden';
|
|
29
|
+
const statusAttribute = banner ? ` data-race-control-status="${escapeHtml(banner.status)}"` : '';
|
|
30
|
+
const kicker = banner?.kicker ?? 'FIA';
|
|
31
|
+
const title = banner?.title ?? 'Race Control';
|
|
32
|
+
|
|
33
|
+
return `
|
|
34
|
+
<div class="${className}" data-tower-race-control-banner${statusAttribute}${hiddenAttribute}>
|
|
35
|
+
<span data-tower-race-control-kicker>${escapeHtml(kicker)}</span>
|
|
36
|
+
<strong data-tower-race-control-title>${escapeHtml(title)}</strong>
|
|
37
|
+
</div>
|
|
38
|
+
`;
|
|
39
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createLoadingMarkup, escapeHtml } from './templateUtils.js';
|
|
2
|
+
|
|
3
|
+
function buttonHiddenAttribute(isVisible) {
|
|
4
|
+
return isVisible ? '' : ' hidden';
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function createRaceControlsMarkup({
|
|
8
|
+
title,
|
|
9
|
+
kicker,
|
|
10
|
+
backLinkHref,
|
|
11
|
+
backLinkLabel,
|
|
12
|
+
showBackLink,
|
|
13
|
+
}) {
|
|
14
|
+
return `
|
|
15
|
+
<header class="sim-topbar" data-paddock-component="race-controls">
|
|
16
|
+
<a class="sim-backlink" href="${escapeHtml(backLinkHref)}"${buttonHiddenAttribute(showBackLink)}>${escapeHtml(backLinkLabel)}</a>
|
|
17
|
+
<div class="sim-title-block">
|
|
18
|
+
<p class="sim-kicker">${escapeHtml(kicker)}</p>
|
|
19
|
+
<h1>${escapeHtml(title)}</h1>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="sim-controls" aria-label="Race controls">
|
|
22
|
+
${createSafetyCarControlMarkup({ compact: true })}
|
|
23
|
+
<button class="sim-control" type="button" data-restart-race>Restart</button>
|
|
24
|
+
</div>
|
|
25
|
+
${createLoadingMarkup('Race controls')}
|
|
26
|
+
</header>
|
|
27
|
+
`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function createSafetyCarControlMarkup({ compact = false } = {}) {
|
|
31
|
+
const className = compact
|
|
32
|
+
? 'sim-control sim-control--safety'
|
|
33
|
+
: 'sim-control sim-control--safety standalone-control';
|
|
34
|
+
const componentAttribute = compact ? '' : ' data-paddock-component="safety-car-control"';
|
|
35
|
+
return `<button class="${className}" type="button" data-safety-car aria-pressed="false"${componentAttribute}>Safety Car</button>`;
|
|
36
|
+
}
|
package/src/ui/shellTemplate.js
CHANGED
|
@@ -11,9 +11,10 @@ export function createF1SimulatorShell({
|
|
|
11
11
|
kicker,
|
|
12
12
|
backLinkHref,
|
|
13
13
|
backLinkLabel,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
showBackLink,
|
|
15
|
+
totalLaps,
|
|
16
|
+
physicsMode,
|
|
17
|
+
assets,
|
|
17
18
|
ui = {},
|
|
18
19
|
}) {
|
|
19
20
|
const layoutPreset = ui.layoutPreset === 'left-tower-overlay' ? 'left-tower-overlay' : 'standard';
|
|
@@ -24,7 +25,7 @@ export function createF1SimulatorShell({
|
|
|
24
25
|
shellClasses.push(`sim-shell--timing-${timingFit}`);
|
|
25
26
|
shellClasses.push(`sim-shell--race-data-${raceDataBannerSize}`);
|
|
26
27
|
const cameraControls = ui.cameraControls === 'external'
|
|
27
|
-
? `<div class="sim-external-camera-controls">${createCameraControlsMarkup()}</div>`
|
|
28
|
+
? `<div class="sim-external-camera-controls">${createCameraControlsMarkup({ ui })}</div>`
|
|
28
29
|
: '';
|
|
29
30
|
|
|
30
31
|
return `
|
|
@@ -34,7 +35,7 @@ export function createF1SimulatorShell({
|
|
|
34
35
|
${cameraControls}
|
|
35
36
|
<div class="sim-grid">
|
|
36
37
|
${createTimingTowerMarkup({ totalLaps, assets })}
|
|
37
|
-
|
|
38
|
+
${createRaceCanvasMarkup({ includeRaceDataPanel: ui.showRaceDataPanel !== false, assets, totalLaps, physicsMode, ui })}
|
|
38
39
|
${createTelemetryPanelMarkup({ assets, ui })}
|
|
39
40
|
</div>
|
|
40
41
|
</section>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { createCameraControlsMarkup } from './cameraControlsTemplate.js';
|
|
2
|
+
import { createRaceCanvasMarkup } from './raceCanvasTemplate.js';
|
|
3
|
+
import { createSafetyCarControlMarkup } from './raceControlsTemplate.js';
|
|
4
|
+
import { createTelemetryPanelMarkup } from './telemetryTemplates.js';
|
|
5
|
+
|
|
6
|
+
let telemetryDrawerIdSequence = 0;
|
|
7
|
+
|
|
8
|
+
function createTelemetryDrawerId() {
|
|
9
|
+
telemetryDrawerIdSequence += 1;
|
|
10
|
+
return `paddock-telemetry-drawer-${telemetryDrawerIdSequence}`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function createRaceTelemetryDrawerMarkup(options, {
|
|
14
|
+
timingTowerVerticalFit,
|
|
15
|
+
drawerInitiallyOpen = false,
|
|
16
|
+
raceDataTelemetryDetail = options.ui?.raceDataTelemetryDetail,
|
|
17
|
+
} = {}) {
|
|
18
|
+
const openClass = drawerInitiallyOpen ? ' is-telemetry-open' : '';
|
|
19
|
+
const drawerId = createTelemetryDrawerId();
|
|
20
|
+
const drawerOptions = {
|
|
21
|
+
...options,
|
|
22
|
+
ui: {
|
|
23
|
+
...(options.ui ?? {}),
|
|
24
|
+
cameraControls: false,
|
|
25
|
+
raceDataTelemetryDetail: Boolean(raceDataTelemetryDetail),
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
const showCameraControls = options.ui?.cameraControls !== false;
|
|
29
|
+
return `
|
|
30
|
+
<section class="race-telemetry-drawer${openClass}" data-paddock-component="race-telemetry-drawer" data-race-telemetry-drawer aria-label="Race view with telemetry drawer">
|
|
31
|
+
<div class="race-telemetry-drawer__toolbar" aria-label="Race workbench controls">
|
|
32
|
+
${showCameraControls ? createCameraControlsMarkup({ showSimulationSpeed: true, ui: options.ui }) : ''}
|
|
33
|
+
<div class="race-telemetry-drawer__controls">
|
|
34
|
+
${createSafetyCarControlMarkup({ compact: true })}
|
|
35
|
+
<button class="telemetry-drawer-toggle" type="button" data-telemetry-drawer-toggle aria-expanded="${drawerInitiallyOpen ? 'true' : 'false'}" aria-controls="${drawerId}">
|
|
36
|
+
${drawerInitiallyOpen ? 'Close telemetry' : 'Telemetry'}
|
|
37
|
+
</button>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="race-telemetry-drawer__race">
|
|
41
|
+
${createRaceCanvasMarkup({
|
|
42
|
+
...drawerOptions,
|
|
43
|
+
includeRaceDataPanel: true,
|
|
44
|
+
includeTimingTower: true,
|
|
45
|
+
timingTowerVerticalFit,
|
|
46
|
+
})}
|
|
47
|
+
</div>
|
|
48
|
+
<aside id="${drawerId}" class="telemetry-drawer" data-telemetry-drawer aria-label="Telemetry drawer" aria-hidden="${drawerInitiallyOpen ? 'false' : 'true'}"${drawerInitiallyOpen ? '' : ' inert'}>
|
|
49
|
+
<div class="telemetry-drawer__content">
|
|
50
|
+
${createTelemetryPanelMarkup(options, { includeOverview: false })}
|
|
51
|
+
</div>
|
|
52
|
+
</aside>
|
|
53
|
+
</section>
|
|
54
|
+
`;
|
|
55
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { createCarDriverOverviewMarkup } from './carOverviewTemplate.js';
|
|
2
|
+
import { createLoadingMarkup } from './templateUtils.js';
|
|
3
|
+
|
|
4
|
+
function getTelemetryModules(ui = {}) {
|
|
5
|
+
const defaults = {
|
|
6
|
+
core: true,
|
|
7
|
+
sectors: true,
|
|
8
|
+
lapTimes: true,
|
|
9
|
+
sectorTimes: true,
|
|
10
|
+
};
|
|
11
|
+
const modules = ui.telemetryModules;
|
|
12
|
+
if (modules === false) return Object.fromEntries(Object.keys(defaults).map((name) => [name, false]));
|
|
13
|
+
if (Array.isArray(modules)) {
|
|
14
|
+
const requested = new Set(modules);
|
|
15
|
+
return Object.fromEntries(Object.keys(defaults).map((name) => [name, requested.has(name)]));
|
|
16
|
+
}
|
|
17
|
+
if (!modules || typeof modules !== 'object') return defaults;
|
|
18
|
+
return Object.fromEntries(Object.entries(defaults).map(([name, fallback]) => [
|
|
19
|
+
name,
|
|
20
|
+
modules[name] == null ? fallback : Boolean(modules[name]),
|
|
21
|
+
]));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getTelemetryModuleClass(componentName) {
|
|
25
|
+
return `sim-telemetry telemetry-component telemetry-component--${componentName}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function createTelemetryCoreMarkup() {
|
|
29
|
+
return `
|
|
30
|
+
<section class="${getTelemetryModuleClass('core')}" data-paddock-component="telemetry-core" aria-label="Selected car core telemetry">
|
|
31
|
+
<div class="telemetry-header">
|
|
32
|
+
<span data-selected-code>---</span>
|
|
33
|
+
<strong data-selected-name>Select car</strong>
|
|
34
|
+
</div>
|
|
35
|
+
<dl class="telemetry-grid">
|
|
36
|
+
<div><dt>Speed</dt><dd data-telemetry-speed>0 km/h</dd></div>
|
|
37
|
+
<div><dt>Throttle</dt><dd data-telemetry-throttle>0%</dd></div>
|
|
38
|
+
<div><dt>Brake</dt><dd data-telemetry-brake>0%</dd></div>
|
|
39
|
+
<div><dt>Tyres</dt><dd data-telemetry-tyres>0%</dd></div>
|
|
40
|
+
<div><dt>DRS</dt><dd data-telemetry-drs>OFF</dd></div>
|
|
41
|
+
<div><dt>Surface</dt><dd data-telemetry-surface>TRACK</dd></div>
|
|
42
|
+
<div><dt>Grip</dt><dd data-telemetry-grip>0%</dd></div>
|
|
43
|
+
<div><dt>Lat G</dt><dd data-telemetry-lateral-g>0.0g</dd></div>
|
|
44
|
+
<div><dt>Slip</dt><dd data-telemetry-slip-angle>0.0 deg</dd></div>
|
|
45
|
+
<div><dt>Stability</dt><dd data-telemetry-stability>STABLE</dd></div>
|
|
46
|
+
<div><dt>Interval</dt><dd data-telemetry-gap>--</dd></div>
|
|
47
|
+
<div><dt>Leader</dt><dd data-telemetry-leader-gap>--</dd></div>
|
|
48
|
+
</dl>
|
|
49
|
+
${createLoadingMarkup('Core telemetry')}
|
|
50
|
+
</section>
|
|
51
|
+
`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function createTelemetrySectorsMarkup() {
|
|
55
|
+
return `
|
|
56
|
+
<section class="${getTelemetryModuleClass('sectors')} telemetry-sector-strip" data-paddock-component="telemetry-sectors" data-telemetry-sector-strip aria-label="Sector progress">
|
|
57
|
+
<div class="telemetry-module-header">
|
|
58
|
+
<span>Sector map</span>
|
|
59
|
+
<strong data-telemetry-current-sector>S1</strong>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="telemetry-sector-bars">
|
|
62
|
+
${[1, 2, 3].map((sector) => `
|
|
63
|
+
<div class="telemetry-sector-bar" data-telemetry-sector-bar="${sector}" style="--sector-fill: 0%">
|
|
64
|
+
<span>S${sector}</span>
|
|
65
|
+
<strong data-telemetry-sector-time="${sector}">--</strong>
|
|
66
|
+
</div>
|
|
67
|
+
`).join('')}
|
|
68
|
+
</div>
|
|
69
|
+
${createLoadingMarkup('Sector telemetry')}
|
|
70
|
+
</section>
|
|
71
|
+
`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function createTelemetryLapTimesMarkup() {
|
|
75
|
+
return `
|
|
76
|
+
<section class="${getTelemetryModuleClass('lap-times')} telemetry-lap-module" data-paddock-component="telemetry-lap-times" aria-label="Lap timing">
|
|
77
|
+
<div class="telemetry-module-header">
|
|
78
|
+
<span>Lap timing</span>
|
|
79
|
+
<strong data-telemetry-completed-laps>0 laps</strong>
|
|
80
|
+
</div>
|
|
81
|
+
<table class="telemetry-lap-table" data-telemetry-lap-table>
|
|
82
|
+
<tbody>
|
|
83
|
+
<tr><th scope="row">Current</th><td data-telemetry-current-lap-time>--</td></tr>
|
|
84
|
+
<tr><th scope="row">Last</th><td data-telemetry-last-lap-time>--</td></tr>
|
|
85
|
+
<tr><th scope="row">Best</th><td data-telemetry-best-lap-time>--</td></tr>
|
|
86
|
+
</tbody>
|
|
87
|
+
</table>
|
|
88
|
+
${createLoadingMarkup('Lap telemetry')}
|
|
89
|
+
</section>
|
|
90
|
+
`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function createTelemetrySectorTimesMarkup() {
|
|
94
|
+
return `
|
|
95
|
+
<section class="${getTelemetryModuleClass('sector-times')} telemetry-sector-table-module" data-paddock-component="telemetry-sector-times" aria-label="Sector timing table">
|
|
96
|
+
<div class="telemetry-module-header">
|
|
97
|
+
<span>Sector timing</span>
|
|
98
|
+
<strong>Last / Best</strong>
|
|
99
|
+
</div>
|
|
100
|
+
<table class="telemetry-sector-table" data-telemetry-sector-table>
|
|
101
|
+
<thead>
|
|
102
|
+
<tr><th scope="col">Sector</th><th scope="col">Last</th><th scope="col">Best</th></tr>
|
|
103
|
+
</thead>
|
|
104
|
+
<tbody>
|
|
105
|
+
${[1, 2, 3].map((sector) => `
|
|
106
|
+
<tr data-telemetry-sector-row="${sector}">
|
|
107
|
+
<th scope="row">S${sector}</th>
|
|
108
|
+
<td data-telemetry-sector-last="${sector}">--</td>
|
|
109
|
+
<td data-telemetry-sector-best="${sector}">--</td>
|
|
110
|
+
</tr>
|
|
111
|
+
`).join('')}
|
|
112
|
+
</tbody>
|
|
113
|
+
</table>
|
|
114
|
+
${createLoadingMarkup('Sector table')}
|
|
115
|
+
</section>
|
|
116
|
+
`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function createTelemetryComponentMarkup(options, modules = getTelemetryModules(options.ui)) {
|
|
120
|
+
return `
|
|
121
|
+
${modules.core ? createTelemetryCoreMarkup(options) : ''}
|
|
122
|
+
${modules.sectors ? createTelemetrySectorsMarkup(options) : ''}
|
|
123
|
+
${modules.lapTimes ? createTelemetryLapTimesMarkup(options) : ''}
|
|
124
|
+
${modules.sectorTimes ? createTelemetrySectorTimesMarkup(options) : ''}
|
|
125
|
+
`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function createTelemetryPanelMarkup(options, { includeOverview = options.ui?.telemetryIncludesOverview !== false } = {}) {
|
|
129
|
+
const modules = getTelemetryModules(options.ui);
|
|
130
|
+
return `
|
|
131
|
+
<aside class="telemetry-stack" data-paddock-component="telemetry-stack" aria-label="Selected car telemetry stack">
|
|
132
|
+
${createTelemetryComponentMarkup(options, modules)}
|
|
133
|
+
${includeOverview ? createCarDriverOverviewMarkup(options) : ''}
|
|
134
|
+
</aside>
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function escapeHtml(value) {
|
|
2
|
+
return String(value ?? '')
|
|
3
|
+
.replaceAll('&', '&')
|
|
4
|
+
.replaceAll('<', '<')
|
|
5
|
+
.replaceAll('>', '>')
|
|
6
|
+
.replaceAll('"', '"')
|
|
7
|
+
.replaceAll("'", ''');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function createLoadingMarkup(label = 'Loading') {
|
|
11
|
+
return `
|
|
12
|
+
<div class="paddock-loading" data-paddock-loading aria-label="${escapeHtml(label)} loading">
|
|
13
|
+
<div class="paddock-loading__lights" aria-hidden="true">
|
|
14
|
+
<span></span>
|
|
15
|
+
<span></span>
|
|
16
|
+
<span></span>
|
|
17
|
+
<span></span>
|
|
18
|
+
<span></span>
|
|
19
|
+
</div>
|
|
20
|
+
<span class="paddock-loading__label">${escapeHtml(label)}</span>
|
|
21
|
+
</div>
|
|
22
|
+
`;
|
|
23
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createRaceControlStatusBannerMarkup } from './raceControlStatusBanner.js';
|
|
2
|
+
import { createLoadingMarkup, escapeHtml } from './templateUtils.js';
|
|
3
|
+
|
|
4
|
+
export function createTimingTowerMarkup({ totalLaps, assets }) {
|
|
5
|
+
return `
|
|
6
|
+
<aside class="sim-timing broadcast-tower" data-paddock-component="timing-tower" data-timing-tower aria-label="Timing tower">
|
|
7
|
+
<div class="broadcast-tower-frame">
|
|
8
|
+
<div class="broadcast-brand">
|
|
9
|
+
<img class="broadcast-f1-logo" src="${escapeHtml(assets.f1Logo)}" alt="F1" />
|
|
10
|
+
</div>
|
|
11
|
+
<div class="broadcast-lap">
|
|
12
|
+
<span>Lap</span>
|
|
13
|
+
<strong data-tower-lap-readout>1</strong>
|
|
14
|
+
<span>/</span>
|
|
15
|
+
<span data-tower-total-laps>${escapeHtml(totalLaps)}</span>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="broadcast-gap-mode" role="group" aria-label="Timing gap mode">
|
|
18
|
+
<button type="button" data-timing-gap-mode="interval" aria-pressed="true">Int</button>
|
|
19
|
+
<button type="button" data-timing-gap-mode="leader" aria-pressed="false">Gap</button>
|
|
20
|
+
</div>
|
|
21
|
+
${createRaceControlStatusBannerMarkup()}
|
|
22
|
+
<div class="broadcast-column-head" aria-hidden="true">
|
|
23
|
+
<span>Pos</span>
|
|
24
|
+
<span>Team</span>
|
|
25
|
+
<span>Project</span>
|
|
26
|
+
<span data-timing-gap-label>Int</span>
|
|
27
|
+
<span>Tyre</span>
|
|
28
|
+
</div>
|
|
29
|
+
<ol class="timing-list" data-timing-list></ol>
|
|
30
|
+
</div>
|
|
31
|
+
${createLoadingMarkup('Timing tower')}
|
|
32
|
+
</aside>
|
|
33
|
+
`;
|
|
34
|
+
}
|