@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
package/docs/index.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# PaddockJS Documentation
|
|
2
|
+
|
|
3
|
+
PaddockJS is a browser-mounted, data-driven F1-style race simulator component. It is designed to live as its own package and be installed into host websites such as the portfolio site.
|
|
4
|
+
|
|
5
|
+
Use these docs as the package source of truth:
|
|
6
|
+
|
|
7
|
+
- [System Specs](system_specs.md): expected package behavior, public API, runtime guarantees, and verification standards.
|
|
8
|
+
- [Rules](rules.md): race-control behavior, DRS, safety car, starts, ordering, contact handling, and simulation rules.
|
|
9
|
+
- [Concepts](concepts.md): vocabulary used by the simulator.
|
|
10
|
+
- [Data Contract](data_contract.md): shape of host-provided drivers, car pairings, ratings, assets, and callbacks.
|
|
11
|
+
- [Architecture](architecture.md): module ownership and data/control flow.
|
|
12
|
+
- [Data Model Notes](data_model.md): lower-level simulator data model notes kept with package docs.
|
|
13
|
+
- [Learnings](learnings.md): implementation lessons and layout verification rules learned from simulator integration issues.
|
|
14
|
+
|
|
15
|
+
When simulator behavior changes, update the relevant doc in this folder in the same change.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Learnings
|
|
2
|
+
|
|
3
|
+
This file records implementation lessons that should influence future PaddockJS changes.
|
|
4
|
+
|
|
5
|
+
## Overlay Layouts Need Internal Safe Areas
|
|
6
|
+
|
|
7
|
+
When a package-owned preset overlays one component on top of another, the preset must reserve a safe area for every affected layer:
|
|
8
|
+
|
|
9
|
+
- The visual overlay itself.
|
|
10
|
+
- Canvas camera framing.
|
|
11
|
+
- Canvas-hosted controls.
|
|
12
|
+
- Start lights.
|
|
13
|
+
- Lower-third and radio banners, unless the intended broadcast treatment is to overlay them across the full combined grid.
|
|
14
|
+
- Any internal rows, headers, grids, or readouts inside the overlay.
|
|
15
|
+
|
|
16
|
+
The left timing-tower overlay exposed this risk. Shrinking the tower container was not enough, because the timing rows still used the wider default timing-column grid. The result was a horizontal scrollbar inside the tower entries even though the outer tower size was package-controlled.
|
|
17
|
+
|
|
18
|
+
The rule is: do not only constrain the outer component. Also verify that all child grids and fixed-width pieces can compress within the new preset.
|
|
19
|
+
|
|
20
|
+
## Presets Own Their Internal Ratios
|
|
21
|
+
|
|
22
|
+
For all-in-one layout presets, hosts may scale the full mounted simulator through the container. Hosts should not be given raw width, max-width, or horizontal-ratio options for internal preset pieces.
|
|
23
|
+
|
|
24
|
+
If a host needs full layout ownership, it should use the composable API and mount package-owned pieces separately:
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
const simulator = createPaddockSimulator(options);
|
|
28
|
+
simulator.mountRaceCanvas(canvasRoot, { includeRaceDataPanel: true });
|
|
29
|
+
simulator.mountTimingTower(timingRoot);
|
|
30
|
+
await simulator.start();
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Avoid adding public options like `timingTowerWidth`, `timingTowerMaxWidth`, or arbitrary column ratios. Prefer named package-owned variants if a second layout is genuinely needed.
|
|
34
|
+
|
|
35
|
+
When the race-data lower-third must overlay the timing tower while staying clipped inside the race window, keep it inside the race canvas with `includeRaceDataPanel: true`. Mounting it as a sibling root gives the host responsibility for stacking contexts and can put the banner underneath the timing tower.
|
|
36
|
+
|
|
37
|
+
## Layout Verification Standard
|
|
38
|
+
|
|
39
|
+
For visual layout changes, verify more than the happy path screenshot:
|
|
40
|
+
|
|
41
|
+
- Desktop, tablet, and narrow mobile widths.
|
|
42
|
+
- Standard and overlay presets.
|
|
43
|
+
- `scrollWidth <= clientWidth` for shell, canvas panel, timing tower, timing list, timing rows, column header, race-data panel, telemetry, car overview, camera controls, and topbar controls.
|
|
44
|
+
- Overlay safe-area geometry: camera controls must start to the right of the overlay tower, while race-data banners are allowed to overlay the tower but must remain centered and clipped inside the race window.
|
|
45
|
+
- Browser smoke against an installed/bundled host, not only string-based unit tests.
|
|
46
|
+
|
|
47
|
+
Unit tests should cover the contract, but browser geometry checks catch CSS interactions that tests can miss.
|
package/docs/rules.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Race Rules
|
|
2
|
+
|
|
3
|
+
This file documents the rules currently implemented by the simulator. If the race engine changes, update this file with the behavior change.
|
|
4
|
+
|
|
5
|
+
## Default Race Rules
|
|
6
|
+
|
|
7
|
+
Default rules are defined in `src/simulation/raceSimulation.js` as `DEFAULT_RULES`:
|
|
8
|
+
|
|
9
|
+
- DRS detection gap: `1` second.
|
|
10
|
+
- Safety car speed: `46` world units per second.
|
|
11
|
+
- Safety car lead distance: `122` world units.
|
|
12
|
+
- Safety car queue gap: `128` world units.
|
|
13
|
+
- Collision restitution: `0.18`.
|
|
14
|
+
- Standing start enabled by default.
|
|
15
|
+
- Start lights: `5`.
|
|
16
|
+
- Start light interval: `0.72` seconds.
|
|
17
|
+
- Lights-out hold: `0.78` seconds.
|
|
18
|
+
|
|
19
|
+
## Race Modes
|
|
20
|
+
|
|
21
|
+
The simulator has three race-control modes:
|
|
22
|
+
|
|
23
|
+
- `pre-start`: cars are grid locked while start lights run.
|
|
24
|
+
- `green`: normal racing.
|
|
25
|
+
- `safety-car`: order is frozen and cars queue behind the safety car.
|
|
26
|
+
|
|
27
|
+
Safety car deployment is ignored during `pre-start`.
|
|
28
|
+
|
|
29
|
+
## Standing Start
|
|
30
|
+
|
|
31
|
+
When standing start is enabled:
|
|
32
|
+
|
|
33
|
+
- Cars begin in staggered grid slots.
|
|
34
|
+
- Cars stay locked until the start-light sequence releases them.
|
|
35
|
+
- Start lights increment over time.
|
|
36
|
+
- When lights go out, cars release and race mode becomes `green`.
|
|
37
|
+
|
|
38
|
+
When standing start is disabled through rules, race mode starts as `green`.
|
|
39
|
+
|
|
40
|
+
## Ordering And Timing
|
|
41
|
+
|
|
42
|
+
Race order is based on `raceDistance`, descending. Ties fall back to original driver index.
|
|
43
|
+
|
|
44
|
+
During safety car, order is frozen at deployment time. That prevents passing from reshuffling the timing tower while the safety car is active.
|
|
45
|
+
|
|
46
|
+
Timing history is sampled per car and used to estimate:
|
|
47
|
+
|
|
48
|
+
- Interval to the car ahead as `intervalAheadSeconds` / `gapAheadSeconds`.
|
|
49
|
+
- Cumulative gap to the leader as `leaderGapSeconds`.
|
|
50
|
+
- DRS detection timing.
|
|
51
|
+
|
|
52
|
+
The timing tower can switch at runtime between `Int` mode, which displays interval to the car ahead, and `Gap` mode, which displays cumulative gap to the leader. Timing continues to be calculated during pre-start, safety-car, and post-finish states even when the UI shows state labels such as `Grid`, `SC`, or `FIN`.
|
|
53
|
+
|
|
54
|
+
## Units
|
|
55
|
+
|
|
56
|
+
The race engine uses simulator units internally. `src/simulation/units.js` converts simulator distance and speed to public meter and km/h values. The current speed calibration maps the simulation maximum speed to an F1-like `330 km/h`; rendered car sprite dimensions are a visual scale and are not used as the physical distance scale.
|
|
57
|
+
|
|
58
|
+
## Laps
|
|
59
|
+
|
|
60
|
+
Lap is computed from each car's cumulative race distance over the track length. Total laps are provided by mount options and default to `10`.
|
|
61
|
+
|
|
62
|
+
Each car is marked `finished` when it reaches `track.length * totalLaps`. The first finisher becomes `winner`, receives classified rank `1`, and emits a `car-finish` event, but the race keeps running until every car has crossed the finish distance.
|
|
63
|
+
|
|
64
|
+
When all cars have finished, the simulator records `finishedAt` and final `classification`, emits a `race-finish` event, freezes order to the classified result, deploys the safety car, and switches race mode to `safety-car`. Finished cars keep circulating under safety-car behavior instead of hard-stopping. The current implementation does not yet implement pit stops, penalties, or championship scoring.
|
|
65
|
+
|
|
66
|
+
## DRS
|
|
67
|
+
|
|
68
|
+
DRS behavior:
|
|
69
|
+
|
|
70
|
+
- DRS is disabled during safety car.
|
|
71
|
+
- Each track has DRS zones.
|
|
72
|
+
- A car latches into a DRS zone when it crosses that zone start.
|
|
73
|
+
- A car becomes DRS eligible if it was close enough to the car ahead at the relevant detection crossing.
|
|
74
|
+
- The current detection window is controlled by `drsDetectionSeconds`.
|
|
75
|
+
- When eligible inside the latched zone, `drsActive` becomes true.
|
|
76
|
+
|
|
77
|
+
Physics effect:
|
|
78
|
+
|
|
79
|
+
- Active DRS reduces drag through `src/simulation/vehiclePhysics.js`.
|
|
80
|
+
- The current drag multiplier is `0.42`.
|
|
81
|
+
|
|
82
|
+
Visual effect:
|
|
83
|
+
|
|
84
|
+
- Active DRS creates cyan trail segments behind the car in `src/app/F1SimulatorApp.js`.
|
|
85
|
+
|
|
86
|
+
## Safety Car
|
|
87
|
+
|
|
88
|
+
Safety car behavior:
|
|
89
|
+
|
|
90
|
+
- Deployment switches race mode to `safety-car`.
|
|
91
|
+
- Race order freezes.
|
|
92
|
+
- DRS state is cleared for all cars.
|
|
93
|
+
- Driver aggression is reduced.
|
|
94
|
+
- Cars target a queue slot behind the safety car.
|
|
95
|
+
- Timing values continue to be calculated, but gaps in the timing tower display as `SC` for non-leaders.
|
|
96
|
+
|
|
97
|
+
When safety car is cleared:
|
|
98
|
+
|
|
99
|
+
- Race mode returns to `green`.
|
|
100
|
+
- Frozen order is removed.
|
|
101
|
+
- Normal DRS and attacking behavior can resume.
|
|
102
|
+
|
|
103
|
+
## Driver AI
|
|
104
|
+
|
|
105
|
+
Driver control decisions live in `src/simulation/driverController.js`.
|
|
106
|
+
|
|
107
|
+
The AI has separate behavior for:
|
|
108
|
+
|
|
109
|
+
- Grid lock.
|
|
110
|
+
- Green-flag racing.
|
|
111
|
+
- Off-track rejoin.
|
|
112
|
+
- Safety-car queueing.
|
|
113
|
+
|
|
114
|
+
Green-flag behavior uses:
|
|
115
|
+
|
|
116
|
+
- Driver aggression.
|
|
117
|
+
- Racecraft.
|
|
118
|
+
- Risk tolerance.
|
|
119
|
+
- Patience.
|
|
120
|
+
- Current tire energy.
|
|
121
|
+
- Track curvature.
|
|
122
|
+
- Nearby traffic.
|
|
123
|
+
- Preferred and available lane offsets.
|
|
124
|
+
|
|
125
|
+
## Vehicle Physics
|
|
126
|
+
|
|
127
|
+
Vehicle physics live in `src/simulation/vehiclePhysics.js`.
|
|
128
|
+
|
|
129
|
+
The model includes:
|
|
130
|
+
|
|
131
|
+
- Steering target and steering rate.
|
|
132
|
+
- Engine force.
|
|
133
|
+
- Brake force.
|
|
134
|
+
- Drag force.
|
|
135
|
+
- Rolling resistance.
|
|
136
|
+
- Surface grip.
|
|
137
|
+
- Downforce grip.
|
|
138
|
+
- Tire condition.
|
|
139
|
+
- Yaw-rate saturation.
|
|
140
|
+
- Tire energy wear.
|
|
141
|
+
|
|
142
|
+
This is a browser simulation with physically grounded approximations. It is not exact F1 telemetry or a professional motorsport dynamics model.
|
|
143
|
+
|
|
144
|
+
## Surfaces
|
|
145
|
+
|
|
146
|
+
Track state can classify a car as on:
|
|
147
|
+
|
|
148
|
+
- `track`
|
|
149
|
+
- `kerb`
|
|
150
|
+
- `gravel`
|
|
151
|
+
- `grass`
|
|
152
|
+
- `barrier`
|
|
153
|
+
|
|
154
|
+
Surface affects grip, drag, and rolling resistance.
|
|
155
|
+
|
|
156
|
+
## Contact And Collision Handling
|
|
157
|
+
|
|
158
|
+
Collision handling uses:
|
|
159
|
+
|
|
160
|
+
- Oriented bounding-box overlap checks.
|
|
161
|
+
- Longitudinal spacing checks.
|
|
162
|
+
- Limited position correction.
|
|
163
|
+
- Contact velocity response.
|
|
164
|
+
- Yaw nudges.
|
|
165
|
+
- Contact cooldown.
|
|
166
|
+
|
|
167
|
+
The goal is believable traffic spacing and visible contact response, not full rigid-body simulation.
|
|
168
|
+
|
|
169
|
+
## Known Non-Goals For Now
|
|
170
|
+
|
|
171
|
+
These are not currently implemented:
|
|
172
|
+
|
|
173
|
+
- Pit stops.
|
|
174
|
+
- Tire compound strategy.
|
|
175
|
+
- Fuel load strategy.
|
|
176
|
+
- Weather.
|
|
177
|
+
- Penalties.
|
|
178
|
+
- Mechanical failures.
|
|
179
|
+
- Race finish ceremony.
|
|
180
|
+
- Multiplayer controls.
|
|
181
|
+
- Official timing rules, licensing, or real-world team identities.
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# System Specs
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
PaddockJS mounts an interactive F1-style race simulator into a host webpage. The package owns the simulator UI, PixiJS renderer, bundled simulator assets, simulation core, default demo data, and public mount API.
|
|
6
|
+
|
|
7
|
+
The host website owns only:
|
|
8
|
+
|
|
9
|
+
- The root DOM element where the simulator mounts.
|
|
10
|
+
- The driver/project list.
|
|
11
|
+
- Optional driver/car pairing entries.
|
|
12
|
+
- Optional display/config overrides.
|
|
13
|
+
- `onDriverOpen(driver)` navigation behavior.
|
|
14
|
+
|
|
15
|
+
## Public API
|
|
16
|
+
|
|
17
|
+
Main import:
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
import {
|
|
21
|
+
createPaddockSimulator,
|
|
22
|
+
mountF1Simulator,
|
|
23
|
+
} from '@inventure71/paddockjs';
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
All-in-one mount call:
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
const simulator = await mountF1Simulator(root, {
|
|
30
|
+
drivers,
|
|
31
|
+
entries,
|
|
32
|
+
onDriverOpen,
|
|
33
|
+
seed,
|
|
34
|
+
trackSeed,
|
|
35
|
+
totalLaps,
|
|
36
|
+
initialCameraMode,
|
|
37
|
+
preset,
|
|
38
|
+
title,
|
|
39
|
+
kicker,
|
|
40
|
+
backLinkHref,
|
|
41
|
+
backLinkLabel,
|
|
42
|
+
showBackLink,
|
|
43
|
+
ui,
|
|
44
|
+
theme,
|
|
45
|
+
assets,
|
|
46
|
+
onLoadingChange,
|
|
47
|
+
onReady,
|
|
48
|
+
onError,
|
|
49
|
+
onDriverSelect,
|
|
50
|
+
onRaceEvent,
|
|
51
|
+
onLapChange,
|
|
52
|
+
onRaceFinish,
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Composable mount call:
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
const simulator = createPaddockSimulator({
|
|
60
|
+
drivers,
|
|
61
|
+
entries,
|
|
62
|
+
onDriverOpen,
|
|
63
|
+
seed,
|
|
64
|
+
trackSeed,
|
|
65
|
+
totalLaps,
|
|
66
|
+
initialCameraMode,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
simulator.mountRaceControls(controlsRoot);
|
|
70
|
+
simulator.mountCameraControls(cameraControlsRoot);
|
|
71
|
+
simulator.mountSafetyCarControl(safetyCarRoot);
|
|
72
|
+
simulator.mountTimingTower(timingRoot);
|
|
73
|
+
simulator.mountRaceCanvas(canvasRoot, {
|
|
74
|
+
includeRaceDataPanel: true,
|
|
75
|
+
includeTimingTower: true,
|
|
76
|
+
timingTowerVerticalFit: 'scroll',
|
|
77
|
+
});
|
|
78
|
+
simulator.mountTelemetryPanel(telemetryRoot);
|
|
79
|
+
|
|
80
|
+
await simulator.start();
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Standalone helper functions are also exported for host code that prefers function calls:
|
|
84
|
+
|
|
85
|
+
```js
|
|
86
|
+
mountRaceControls(root, simulator);
|
|
87
|
+
mountTimingTower(root, simulator);
|
|
88
|
+
mountRaceCanvas(root, simulator, {
|
|
89
|
+
includeRaceDataPanel: true,
|
|
90
|
+
includeTimingTower: true,
|
|
91
|
+
timingTowerVerticalFit: 'scroll',
|
|
92
|
+
});
|
|
93
|
+
mountTelemetryPanel(root, simulator);
|
|
94
|
+
mountRaceDataPanel(root, simulator);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Returned controller:
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
{
|
|
101
|
+
// Included on composable controllers only:
|
|
102
|
+
mountRaceControls(root),
|
|
103
|
+
mountCameraControls(root),
|
|
104
|
+
mountSafetyCarControl(root),
|
|
105
|
+
mountTimingTower(root),
|
|
106
|
+
mountRaceCanvas(root, { includeRaceDataPanel, includeTimingTower, timingTowerVerticalFit }),
|
|
107
|
+
mountTelemetryPanel(root),
|
|
108
|
+
mountRaceDataPanel(root),
|
|
109
|
+
start(),
|
|
110
|
+
|
|
111
|
+
// Included on both APIs:
|
|
112
|
+
destroy(),
|
|
113
|
+
restart(nextOptions),
|
|
114
|
+
selectDriver(driverId),
|
|
115
|
+
setSafetyCarDeployed(deployed),
|
|
116
|
+
callSafetyCar(),
|
|
117
|
+
clearSafetyCar(),
|
|
118
|
+
toggleSafetyCar(),
|
|
119
|
+
getSnapshot(),
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Required Behavior
|
|
124
|
+
|
|
125
|
+
- Mounting creates the simulator shell inside the provided root.
|
|
126
|
+
- Composable mounting can place controls, timing tower, canvas, telemetry, car/driver overview, and race-data panels into separate host roots.
|
|
127
|
+
- Composable mounting can also place camera controls and a safety-car button into separate host roots while keeping package-owned markup.
|
|
128
|
+
- Composable mounting can embed the timing tower directly inside the race canvas with `mountRaceCanvas(root, { includeTimingTower: true })`.
|
|
129
|
+
- The race canvas is required before `start()` because PixiJS needs a canvas host.
|
|
130
|
+
- Timing tower, telemetry, controls, and race-data panels are optional from a runtime safety perspective; omitted panels simply do not render their readouts.
|
|
131
|
+
- Timing tower entries are fixed rows stacked from the top of the timing list. Row vertical position must be based on rank/index, never distributed by available height or total entry count.
|
|
132
|
+
- Timing tower entries display team icons. The tower can switch at runtime between interval-to-car-ahead timing and cumulative gap-to-leader timing.
|
|
133
|
+
- Snapshots expose calibrated display units through `speedKph`, `distanceMeters`, and `gapMeters`; internal physics remains in simulator units.
|
|
134
|
+
- Entries can include optional `team` metadata. Team color defaults to car color when omitted.
|
|
135
|
+
- Mounted package surfaces show a package-owned red start-light loading overlay until `start()` finishes PixiJS, asset, control, and initial readout initialization.
|
|
136
|
+
- `preset` is a preset-first API. Presets are resolved before explicit host overrides so hosts can use `dashboard`, `timing-overlay`, `compact-race`, or `full-dashboard` as a starting point and still override specific `ui` or `theme` fields.
|
|
137
|
+
- `theme` is the public sizing/color contract. It maps to package CSS variables for `accentColor`, `greenColor`, `yellowColor`, `timingTowerMaxWidth`, and `raceViewMinHeight`.
|
|
138
|
+
- Camera controls can be embedded in the race canvas, externally mounted, or omitted by `ui.cameraControls`.
|
|
139
|
+
- Telemetry can include the car/driver overview directly or stay text-only through `ui.telemetryIncludesOverview` / `mountTelemetryPanel(root, { includeOverview: false })`.
|
|
140
|
+
- The FPS readout can be shown or hidden with `ui.showFps`.
|
|
141
|
+
- `ui.layoutPreset: 'left-tower-overlay'` is a package-owned preset that creates a left broadcast gutter inside the race view, places the timing tower there at the same width as the default timing-board column, frames the PixiJS camera around the remaining usable race area, and keeps camera controls and start lights out of the tower area. In the combined shell, project and radio lower-thirds stay inside the race window while being allowed to cover the timing sidebar.
|
|
142
|
+
- `ui.raceDataBanners.initial` controls the starting lower-third (`'project'`, `'radio'`, or `'hidden'`), and `ui.raceDataBanners.enabled` controls which lower-third types can appear.
|
|
143
|
+
- `ui.raceDataBannerSize` controls lower-third sizing: `'custom'` keeps the default CSS-variable-driven banner size for host tuning, while `'auto'` uses the race space to the right of the timing board when wide enough and overlaps the timing board only when space is constrained.
|
|
144
|
+
- `ui.timingTowerVerticalFit` controls vertical tower behavior in the combined overlay preset: `'expand-race-view'` grows the race window to fit the tower, while `'scroll'` crops the tower area and scrolls timing rows inside it. The same values are accepted as `mountRaceCanvas()` options when `includeTimingTower` embeds the tower in the race canvas.
|
|
145
|
+
- Hosts may scale the whole mounted simulator through the container. The horizontal proportions inside package-owned presets are not public API and should not be configurable through raw width or ratio options. The camera reads the current canvas dimensions so wider or taller host windows reveal more of the race view without needing host-owned camera math. The timing tower has a package-owned max width because overly wide timing boards degrade readability; standalone hosts can constrain vertical height through the mount container and let the timing entries scroll internally.
|
|
146
|
+
- The host does not need to provide simulator assets.
|
|
147
|
+
- The host passes data, not internal DOM.
|
|
148
|
+
- `onDriverOpen(driver)` is the navigation boundary.
|
|
149
|
+
- Lifecycle callbacks are optional: `onLoadingChange`, `onReady`, `onError`, `onDriverSelect`, `onRaceEvent`, `onLapChange`, and `onRaceFinish`. Host callback failures are routed to `onError` when possible and must not stop the simulator loop.
|
|
150
|
+
- Race completion is part of the simulation snapshot. Cars receive individual `finished`, `finishTime`, and `classifiedRank` values as they cross the finish distance. The first finisher sets `raceControl.winner`; final `raceControl.classification` and `raceControl.finished` are set only after every car finishes. At that point race control switches to `safety-car`, the final order freezes, and the field keeps circulating under safety-car behavior.
|
|
151
|
+
- The simulator must stay interactive after being installed through `npm install ../PaddockJS`.
|
|
152
|
+
- The package must build correctly through a browser bundler that supports JavaScript modules, CSS imports, and image imports.
|
|
153
|
+
- The simulation should remain deterministic for the same seed, track seed, drivers, entries, and rules.
|
|
154
|
+
- When `trackSeed` is omitted in a browser mount, the simulator creates a fresh procedural circuit for that mount. Explicit `trackSeed` values are deterministic and cached by seed for repeated mounts.
|
|
155
|
+
- The renderer should target a paced 60 FPS simulation/render loop.
|
|
156
|
+
- The render loop should pause while the race canvas is offscreen or the document is hidden, then resume without catching up the elapsed hidden time. Layout measurements needed for overlay camera safe areas should be cached between resize/layout invalidations.
|
|
157
|
+
|
|
158
|
+
## Current Visible Features
|
|
159
|
+
|
|
160
|
+
- Timing tower with position, team icon, timing code, interval/gap switch, and tire compound.
|
|
161
|
+
- Race canvas rendered with PixiJS.
|
|
162
|
+
- Procedural track rendering with asphalt texture and DRS overlays.
|
|
163
|
+
- Driver selection from cars and timing tower rows.
|
|
164
|
+
- Camera modes: overview, leader, selected, show all.
|
|
165
|
+
Overview is a closer static circuit view centered on the world; show all is the mode that dynamically fits the active pack.
|
|
166
|
+
- Zoom controls.
|
|
167
|
+
- FPS readout.
|
|
168
|
+
- Start lights.
|
|
169
|
+
- Safety car toggle.
|
|
170
|
+
- External safety-car control through controller methods and optional mounted button.
|
|
171
|
+
- Restart button.
|
|
172
|
+
- Selected-car text telemetry.
|
|
173
|
+
- Car/driver overview panel with a center visual, linked stat cells, and a Car/Driver toggle.
|
|
174
|
+
- Project/race data lower-third.
|
|
175
|
+
- Intermittent project radio quotes.
|
|
176
|
+
- Race-finish winner banner and final top-three classification.
|
|
177
|
+
- `Open project` button driven by `onDriverOpen(driver)`.
|
|
178
|
+
|
|
179
|
+
## Runtime Requirements
|
|
180
|
+
|
|
181
|
+
- Browser environment with DOM APIs.
|
|
182
|
+
- A host build tool that handles CSS and image imports. Vite is the currently verified bundler.
|
|
183
|
+
- `pixi.js` available through the package dependency graph.
|
|
184
|
+
|
|
185
|
+
Raw Node imports are not a supported runtime check because the package imports CSS and image assets. Verify through a browser bundler and host page instead.
|
|
186
|
+
|
|
187
|
+
## Verification
|
|
188
|
+
|
|
189
|
+
Run from this package:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
npm run check
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Expected:
|
|
196
|
+
|
|
197
|
+
- All Vitest tests pass.
|
|
198
|
+
- `npm pack --dry-run` succeeds and includes source files plus bundled assets.
|
|
199
|
+
|
|
200
|
+
Run from the portfolio host:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
npm install ../PaddockJS
|
|
204
|
+
npm run check
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Expected:
|
|
208
|
+
|
|
209
|
+
- PaddockJS tests pass through the host script.
|
|
210
|
+
- The host F1 bundle builds.
|
|
211
|
+
- Browser smoke shows shell, canvas, driver rows, FPS readout, and working `Open project` navigation.
|
package/package.json
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@inventure71/paddockjs",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "A data-driven browser F1 simulator component for project portfolios.",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"author": "Matteo Giorgetti",
|
|
8
|
+
"homepage": "https://github.com/Inventure71/PaddockJS#readme",
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/Inventure71/PaddockJS/issues"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/Inventure71/PaddockJS.git"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"f1",
|
|
18
|
+
"formula-1",
|
|
19
|
+
"motorsport",
|
|
20
|
+
"racing",
|
|
21
|
+
"simulator",
|
|
22
|
+
"timing-tower",
|
|
23
|
+
"pixijs",
|
|
24
|
+
"ui-component"
|
|
25
|
+
],
|
|
26
|
+
"sideEffects": [
|
|
27
|
+
"./src/styles.css"
|
|
28
|
+
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=20"
|
|
34
|
+
},
|
|
35
|
+
"main": "src/index.js",
|
|
36
|
+
"types": "src/index.d.ts",
|
|
37
|
+
"exports": {
|
|
38
|
+
".": {
|
|
39
|
+
"types": "./src/index.d.ts",
|
|
40
|
+
"import": "./src/index.js",
|
|
41
|
+
"default": "./src/index.js"
|
|
42
|
+
},
|
|
43
|
+
"./styles.css": "./src/styles.css"
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"AGENTS.md",
|
|
47
|
+
"CHANGELOG.md",
|
|
48
|
+
"LICENSE",
|
|
49
|
+
"assets",
|
|
50
|
+
"docs",
|
|
51
|
+
"src/api",
|
|
52
|
+
"src/app",
|
|
53
|
+
"src/config",
|
|
54
|
+
"src/data",
|
|
55
|
+
"src/rendering",
|
|
56
|
+
"src/simulation",
|
|
57
|
+
"src/ui",
|
|
58
|
+
"src/index.d.ts",
|
|
59
|
+
"src/index.js",
|
|
60
|
+
"src/standalone.js",
|
|
61
|
+
"src/styles.css",
|
|
62
|
+
"README.md"
|
|
63
|
+
],
|
|
64
|
+
"scripts": {
|
|
65
|
+
"test": "vitest run src",
|
|
66
|
+
"types:check": "tsc -p tsconfig.types.json --noEmit",
|
|
67
|
+
"check": "npm test && npm run types:check && npm run pack:dry && npm run showcase:ci",
|
|
68
|
+
"pack:dry": "npm pack --dry-run",
|
|
69
|
+
"showcase:install": "npm --prefix local-preview ci",
|
|
70
|
+
"showcase:dev": "npm --prefix local-preview run dev",
|
|
71
|
+
"showcase:build": "npm --prefix local-preview run build",
|
|
72
|
+
"showcase:ci": "npm --prefix local-preview ci && npm --prefix local-preview run build",
|
|
73
|
+
"changeset": "changeset",
|
|
74
|
+
"version-packages": "changeset version",
|
|
75
|
+
"release": "npm publish --provenance"
|
|
76
|
+
},
|
|
77
|
+
"dependencies": {
|
|
78
|
+
"pixi.js": "8.18.1"
|
|
79
|
+
},
|
|
80
|
+
"devDependencies": {
|
|
81
|
+
"@changesets/cli": "2.29.7",
|
|
82
|
+
"typescript": "5.9.3",
|
|
83
|
+
"vitest": "4.1.5"
|
|
84
|
+
}
|
|
85
|
+
}
|