@inventure71/paddockjs 0.1.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/AGENTS.md +86 -0
- package/CHANGELOG.md +5 -0
- package/LICENSE +176 -0
- package/README.md +165 -0
- package/assets/f1-broadcast-panel-surface.png +0 -0
- package/assets/f1-car-sprite-game.png +0 -0
- package/assets/f1-driver-helmet.png +0 -0
- package/assets/f1-safety-car-sprite.png +0 -0
- package/assets/f1-texture-asphalt.png +0 -0
- package/assets/f1Logo.png +0 -0
- package/docs/architecture.md +209 -0
- package/docs/concepts.md +178 -0
- package/docs/data_contract.md +360 -0
- package/docs/data_model.md +167 -0
- package/docs/index.md +15 -0
- package/docs/learnings.md +47 -0
- package/docs/rules.md +181 -0
- package/docs/system_specs.md +211 -0
- package/package.json +85 -0
- package/src/api/PaddockSimulatorController.js +233 -0
- package/src/app/F1SimulatorApp.js +1293 -0
- package/src/app/domBindings.js +64 -0
- package/src/config/defaultAssets.js +29 -0
- package/src/config/defaultOptions.js +184 -0
- package/src/data/championship.js +225 -0
- package/src/data/customFields.js +14 -0
- package/src/data/demoDrivers.js +134 -0
- package/src/data/driverData.js +71 -0
- package/src/data/normalizeDrivers.js +21 -0
- package/src/data/vehicleData.js +81 -0
- package/src/index.d.ts +399 -0
- package/src/index.js +116 -0
- package/src/rendering/proceduralTrackAsset.js +517 -0
- package/src/rendering/renderSnapshot.js +23 -0
- package/src/simulation/driverController.js +253 -0
- package/src/simulation/raceSimulation.js +933 -0
- package/src/simulation/simMath.js +34 -0
- package/src/simulation/trackModel.js +607 -0
- package/src/simulation/units.js +30 -0
- package/src/simulation/vehiclePhysics.js +104 -0
- package/src/standalone.js +15 -0
- package/src/styles.css +1818 -0
- package/src/ui/componentTemplates.js +229 -0
- package/src/ui/shellTemplate.js +43 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { F1SimulatorApp } from '../app/F1SimulatorApp.js';
|
|
2
|
+
import {
|
|
3
|
+
createCarDriverOverviewMarkup,
|
|
4
|
+
createCameraControlsMarkup,
|
|
5
|
+
createRaceCanvasMarkup,
|
|
6
|
+
createRaceControlsMarkup,
|
|
7
|
+
createRaceDataPanelMarkup,
|
|
8
|
+
createSafetyCarControlMarkup,
|
|
9
|
+
createTelemetryPanelMarkup,
|
|
10
|
+
createTimingTowerMarkup,
|
|
11
|
+
} from '../ui/componentTemplates.js';
|
|
12
|
+
import { applyPaddockThemeCssVariables, resolveF1SimulatorOptions } from '../config/defaultOptions.js';
|
|
13
|
+
|
|
14
|
+
function assertMountTarget(root, label) {
|
|
15
|
+
if (!root || typeof root !== 'object' || !('innerHTML' in root)) {
|
|
16
|
+
throw new Error(`${label} requires a DOM element root.`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function setPackageCssVariables(root, assets, theme) {
|
|
21
|
+
root.classList?.add?.('f1-sim-component');
|
|
22
|
+
root.style?.setProperty?.('--broadcast-panel-surface', `url('${assets.broadcastPanel}')`);
|
|
23
|
+
applyPaddockThemeCssVariables(root, theme);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function mergeResolvedOptions(previousOptions, nextOptions) {
|
|
27
|
+
const resetFromPreset = Object.hasOwn(nextOptions, 'preset');
|
|
28
|
+
const previousUi = resetFromPreset ? {} : previousOptions.ui;
|
|
29
|
+
const previousTheme = resetFromPreset ? {} : previousOptions.theme;
|
|
30
|
+
return {
|
|
31
|
+
...previousOptions,
|
|
32
|
+
...nextOptions,
|
|
33
|
+
ui: {
|
|
34
|
+
...previousUi,
|
|
35
|
+
...(nextOptions.ui ?? {}),
|
|
36
|
+
raceDataBanners: {
|
|
37
|
+
...(previousUi.raceDataBanners ?? {}),
|
|
38
|
+
...(nextOptions.ui?.raceDataBanners ?? {}),
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
theme: {
|
|
42
|
+
...previousTheme,
|
|
43
|
+
...(nextOptions.theme ?? {}),
|
|
44
|
+
},
|
|
45
|
+
assets: {
|
|
46
|
+
...previousOptions.assets,
|
|
47
|
+
...(nextOptions.assets ?? {}),
|
|
48
|
+
trackTextures: {
|
|
49
|
+
...previousOptions.assets.trackTextures,
|
|
50
|
+
...(nextOptions.assets?.trackTextures ?? {}),
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
drivers: nextOptions.drivers ?? previousOptions.drivers,
|
|
54
|
+
entries: nextOptions.entries ?? previousOptions.entries,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function createCompositeRoot(getRoots, getOptions) {
|
|
59
|
+
return {
|
|
60
|
+
style: {
|
|
61
|
+
setProperty(name, value) {
|
|
62
|
+
getRoots().forEach((root) => root.style?.setProperty?.(name, value));
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
querySelector(selector) {
|
|
66
|
+
for (const root of getRoots()) {
|
|
67
|
+
const match = root.querySelector?.(selector);
|
|
68
|
+
if (match) return match;
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
},
|
|
72
|
+
querySelectorAll(selector) {
|
|
73
|
+
return getRoots().flatMap((root) => [...(root.querySelectorAll?.(selector) ?? [])]);
|
|
74
|
+
},
|
|
75
|
+
applyCssVariables() {
|
|
76
|
+
const options = getOptions();
|
|
77
|
+
getRoots().forEach((root) => setPackageCssVariables(root, options.assets, options.theme));
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export class PaddockSimulatorController {
|
|
83
|
+
constructor(options = {}) {
|
|
84
|
+
this.options = resolveF1SimulatorOptions(options);
|
|
85
|
+
this.roots = new Map();
|
|
86
|
+
this.app = null;
|
|
87
|
+
this.compositeRoot = createCompositeRoot(() => [...this.roots.values()], () => this.options);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
mountComponent(root, key, markup) {
|
|
91
|
+
assertMountTarget(root, `mount ${key}`);
|
|
92
|
+
if (this.app) {
|
|
93
|
+
throw new Error('Mount PaddockJS components before calling start().');
|
|
94
|
+
}
|
|
95
|
+
root.innerHTML = markup;
|
|
96
|
+
setPackageCssVariables(root, this.options.assets, this.options.theme);
|
|
97
|
+
this.roots.set(key, root);
|
|
98
|
+
return root;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
mountRaceControls(root) {
|
|
102
|
+
return this.mountComponent(root, 'race-controls', createRaceControlsMarkup(this.options));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
mountCameraControls(root) {
|
|
106
|
+
return this.mountComponent(root, 'camera-controls', createCameraControlsMarkup(this.options));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
mountSafetyCarControl(root) {
|
|
110
|
+
return this.mountComponent(root, 'safety-car-control', createSafetyCarControlMarkup(this.options));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
mountTimingTower(root) {
|
|
114
|
+
return this.mountComponent(root, 'timing-tower', createTimingTowerMarkup(this.options));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
mountRaceCanvas(root, {
|
|
118
|
+
includeRaceDataPanel = false,
|
|
119
|
+
includeTimingTower = false,
|
|
120
|
+
timingTowerVerticalFit,
|
|
121
|
+
} = {}) {
|
|
122
|
+
return this.mountComponent(root, 'race-canvas', createRaceCanvasMarkup({
|
|
123
|
+
...this.options,
|
|
124
|
+
includeRaceDataPanel,
|
|
125
|
+
includeTimingTower,
|
|
126
|
+
timingTowerVerticalFit,
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
mountTelemetryPanel(root, { includeOverview } = {}) {
|
|
131
|
+
return this.mountComponent(root, 'telemetry-panel', createTelemetryPanelMarkup(this.options, { includeOverview }));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
mountCarDriverOverview(root) {
|
|
135
|
+
return this.mountComponent(root, 'car-driver-overview', createCarDriverOverviewMarkup(this.options));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
mountRaceDataPanel(root) {
|
|
139
|
+
return this.mountComponent(root, 'race-data-panel', createRaceDataPanelMarkup(this.options));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
querySelector(selector) {
|
|
143
|
+
return this.compositeRoot.querySelector(selector);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
querySelectorAll(selector) {
|
|
147
|
+
return this.compositeRoot.querySelectorAll(selector);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async start() {
|
|
151
|
+
if (this.app) return this;
|
|
152
|
+
this.compositeRoot.applyCssVariables();
|
|
153
|
+
this.app = new F1SimulatorApp(this.compositeRoot, this.options);
|
|
154
|
+
await this.app.init();
|
|
155
|
+
return this;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
destroy() {
|
|
159
|
+
this.app?.destroy();
|
|
160
|
+
this.app = null;
|
|
161
|
+
this.roots.forEach((root) => {
|
|
162
|
+
root.innerHTML = '';
|
|
163
|
+
});
|
|
164
|
+
this.roots.clear();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
restart(nextOptions = {}) {
|
|
168
|
+
this.options = resolveF1SimulatorOptions(mergeResolvedOptions(this.options, nextOptions));
|
|
169
|
+
this.compositeRoot.applyCssVariables();
|
|
170
|
+
this.app?.restart(this.options);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
selectDriver(driverId) {
|
|
174
|
+
this.app?.selectCar(driverId, { focus: true });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
setSafetyCarDeployed(deployed) {
|
|
178
|
+
this.app?.setSafetyCarDeployed(deployed);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
callSafetyCar() {
|
|
182
|
+
this.setSafetyCarDeployed(true);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
clearSafetyCar() {
|
|
186
|
+
this.setSafetyCarDeployed(false);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
toggleSafetyCar() {
|
|
190
|
+
const active = this.app?.getSnapshot()?.raceControl.mode === 'safety-car';
|
|
191
|
+
this.setSafetyCarDeployed(!active);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
getSnapshot() {
|
|
195
|
+
return this.app?.getSnapshot() ?? null;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function createPaddockSimulator(options = {}) {
|
|
200
|
+
return new PaddockSimulatorController(options);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function mountRaceControls(root, simulator) {
|
|
204
|
+
return simulator.mountRaceControls(root);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function mountCameraControls(root, simulator) {
|
|
208
|
+
return simulator.mountCameraControls(root);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function mountCarDriverOverview(root, simulator) {
|
|
212
|
+
return simulator.mountCarDriverOverview(root);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function mountSafetyCarControl(root, simulator) {
|
|
216
|
+
return simulator.mountSafetyCarControl(root);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function mountTimingTower(root, simulator) {
|
|
220
|
+
return simulator.mountTimingTower(root);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function mountRaceCanvas(root, simulator, options) {
|
|
224
|
+
return simulator.mountRaceCanvas(root, options);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function mountTelemetryPanel(root, simulator, options) {
|
|
228
|
+
return simulator.mountTelemetryPanel(root, options);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function mountRaceDataPanel(root, simulator) {
|
|
232
|
+
return simulator.mountRaceDataPanel(root);
|
|
233
|
+
}
|