@inventure71/paddockjs 4.0.0 → 4.2.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 +48 -0
- package/INSTALL_AND_UPDATE.md +14 -1
- package/README.md +33 -1
- package/docs/architecture.md +3 -1
- package/docs/component_inventory.md +5 -4
- package/docs/composable-layouts.md +2 -0
- package/docs/data_contract.md +5 -1
- package/docs/getting-started.md +33 -0
- package/docs/index.md +4 -2
- package/docs/loading.md +141 -0
- package/docs/system_specs.md +15 -2
- package/docs/testing-reliability-guide.md +62 -0
- package/package.json +16 -9
- package/src/data/index.d.ts +64 -1
- package/src/environment/index.d.ts +75 -12
- package/src/index.d.ts +72 -6
- package/src/placeholder/index.d.ts +11 -0
- package/src/placeholder/index.js +61 -0
- package/src/placeholder.css +72 -0
- package/src/simulation/collisionGeometry.js +7 -9
- package/src/simulation/driver/driverController.js +2 -2
- package/src/simulation/driver/edgeRecovery.js +2 -2
- package/src/simulation/driver/racingControls.js +35 -25
- package/src/simulation/driver/racingLinePlan.js +6 -5
- package/src/simulation/driver/rejoinControls.js +3 -3
- package/src/simulation/driver/trafficScan.js +48 -22
- package/src/simulation/participants/participantInteractions.js +24 -15
- package/src/simulation/race/raceStep.js +19 -10
- package/src/simulation/race/safetyCar.js +2 -1
- package/src/simulation/rules/pitLaneSpeedingSteward.js +1 -1
- package/src/simulation/rules/rulesReview.js +19 -9
- package/src/simulation/rules/trackLimitsSteward.js +2 -2
- package/src/simulation/timing/sectorPerformance.js +11 -3
- package/src/simulation/track/spatialQueries.js +80 -11
- package/src/simulation/track/trackModel.js +10 -1
- package/src/simulation/vehicle/contactResolution.js +4 -4
- package/src/simulation/vehicle/vehicleGeometry.js +87 -49
- package/src/simulation/vehicle/vehiclePhysics.js +7 -2
- package/src/simulation/vehicle/vehicleSnapshots.js +5 -2
- package/src/simulation/vehicle/vehicleState.js +3 -2
- package/src/ui/raceCanvasTemplate.js +3 -2
- package/src/ui/raceControlsTemplate.js +1 -0
- package/src/ui/telemetryDrawerTemplate.js +1 -0
- package/src/ui/telemetryTemplates.js +1 -0
- package/examples/train-basic-policy.mjs +0 -276
- package/examples/trainingData.mjs +0 -133
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inventure71/paddockjs",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A data-driven browser F1 simulator component for host websites.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"ui-component"
|
|
25
25
|
],
|
|
26
26
|
"sideEffects": [
|
|
27
|
-
"./src/styles.css"
|
|
27
|
+
"./src/styles.css",
|
|
28
|
+
"./src/placeholder.css"
|
|
28
29
|
],
|
|
29
30
|
"publishConfig": {
|
|
30
31
|
"access": "public"
|
|
@@ -50,7 +51,13 @@
|
|
|
50
51
|
"import": "./src/data/index.js",
|
|
51
52
|
"default": "./src/data/index.js"
|
|
52
53
|
},
|
|
53
|
-
"./
|
|
54
|
+
"./placeholder": {
|
|
55
|
+
"types": "./src/placeholder/index.d.ts",
|
|
56
|
+
"import": "./src/placeholder/index.js",
|
|
57
|
+
"default": "./src/placeholder/index.js"
|
|
58
|
+
},
|
|
59
|
+
"./styles.css": "./src/styles.css",
|
|
60
|
+
"./placeholder.css": "./src/placeholder.css"
|
|
54
61
|
},
|
|
55
62
|
"files": [
|
|
56
63
|
"CHANGELOG.md",
|
|
@@ -61,19 +68,19 @@
|
|
|
61
68
|
"examples/python/README.md",
|
|
62
69
|
"examples/python/base_policy_server.py",
|
|
63
70
|
"examples/python/requirements.txt",
|
|
64
|
-
"examples/train-basic-policy.mjs",
|
|
65
|
-
"examples/trainingData.mjs",
|
|
66
71
|
"src/api",
|
|
67
72
|
"src/app",
|
|
68
73
|
"src/config",
|
|
69
74
|
"src/data",
|
|
70
75
|
"src/environment",
|
|
76
|
+
"src/placeholder",
|
|
71
77
|
"src/rendering",
|
|
72
78
|
"src/simulation",
|
|
73
79
|
"src/ui",
|
|
74
80
|
"src/index.d.ts",
|
|
75
81
|
"src/index.js",
|
|
76
82
|
"src/styles.css",
|
|
83
|
+
"src/placeholder.css",
|
|
77
84
|
"README.md"
|
|
78
85
|
],
|
|
79
86
|
"scripts": {
|
|
@@ -81,10 +88,11 @@
|
|
|
81
88
|
"test:fast": "node scripts/run-vitest.mjs",
|
|
82
89
|
"types:check": "tsc -p tsconfig.types.json --noEmit",
|
|
83
90
|
"docs:check": "node scripts/docs-check.mjs",
|
|
84
|
-
"check": "npm run docs:check && npm run test:fast && npm run types:check && npm run pack:dry && npm run consumer:smoke && npm run showcase:ci && npm run browser:smoke:quick -- --skip-build",
|
|
85
|
-
"check:release": "npm run docs:check && npm test && npm run types:check && npm run pack:dry && npm run consumer:smoke && npm run showcase:ci && npm run browser:smoke:full -- --skip-build",
|
|
91
|
+
"check": "npm run docs:check && npm run test:fast && npm run types:check && npm run pack:dry && npm run consumer:smoke && npm run consumer:bundle-boundaries && npm run showcase:ci && npm run browser:smoke:quick -- --skip-build",
|
|
92
|
+
"check:release": "npm run docs:check && npm test && npm run types:check && npm run pack:dry && npm run consumer:smoke && npm run consumer:bundle-boundaries && npm run showcase:ci && npm run browser:smoke:full -- --skip-build",
|
|
86
93
|
"pack:dry": "npm pack --dry-run",
|
|
87
94
|
"consumer:smoke": "node scripts/consumer-package-smoke.mjs",
|
|
95
|
+
"consumer:bundle-boundaries": "node scripts/consumer-bundle-boundaries.mjs",
|
|
88
96
|
"browser:smoke": "node smoke/browser-smoke.mjs",
|
|
89
97
|
"browser:smoke:quick": "node smoke/browser-smoke.mjs --quick",
|
|
90
98
|
"browser:smoke:full": "node smoke/browser-smoke.mjs --full",
|
|
@@ -95,8 +103,7 @@
|
|
|
95
103
|
"showcase:ci": "npm --prefix local-preview ci && npm --prefix local-preview run build",
|
|
96
104
|
"changeset": "changeset",
|
|
97
105
|
"version-packages": "changeset version",
|
|
98
|
-
"release": "npm publish --provenance"
|
|
99
|
-
"example:train": "node examples/train-basic-policy.mjs"
|
|
106
|
+
"release": "npm publish --provenance"
|
|
100
107
|
},
|
|
101
108
|
"dependencies": {
|
|
102
109
|
"pixi.js": "8.18.1"
|
package/src/data/index.d.ts
CHANGED
|
@@ -38,6 +38,69 @@ export interface PaddockProceduralTrackOptions {
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
export interface PaddockTrackPoint {
|
|
42
|
+
x: number;
|
|
43
|
+
y: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface PaddockDrsZone {
|
|
47
|
+
id: string;
|
|
48
|
+
startRatio: number;
|
|
49
|
+
endRatio: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface PaddockResolvedProceduralTrackOptions {
|
|
53
|
+
profile: PaddockProceduralTrackProfile;
|
|
54
|
+
length: {
|
|
55
|
+
min: number;
|
|
56
|
+
max: number;
|
|
57
|
+
};
|
|
58
|
+
startStraight: {
|
|
59
|
+
grid: number;
|
|
60
|
+
exit: number;
|
|
61
|
+
blend: number;
|
|
62
|
+
lockExtra: number;
|
|
63
|
+
};
|
|
64
|
+
pitLane: {
|
|
65
|
+
enabled: boolean;
|
|
66
|
+
};
|
|
67
|
+
shape: {
|
|
68
|
+
scale: number;
|
|
69
|
+
cornerDensity: number;
|
|
70
|
+
variation: number;
|
|
71
|
+
};
|
|
72
|
+
validation: {
|
|
73
|
+
minClearanceMultiplier: number;
|
|
74
|
+
minShapeVariation: number;
|
|
75
|
+
minNonAdjacentArcDistance: number;
|
|
76
|
+
maxLocalTurnRadians: number;
|
|
77
|
+
maxSampleHeadingDeltaRadians: number;
|
|
78
|
+
};
|
|
79
|
+
attempts: {
|
|
80
|
+
primary: number;
|
|
81
|
+
fallback: number;
|
|
82
|
+
};
|
|
83
|
+
cacheKey: string;
|
|
84
|
+
[key: string]: unknown;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface PaddockProceduralTrack {
|
|
88
|
+
name: string;
|
|
89
|
+
width: number;
|
|
90
|
+
kerbWidth: number;
|
|
91
|
+
gravelWidth: number;
|
|
92
|
+
runoffWidth: number;
|
|
93
|
+
barrierWidth: number;
|
|
94
|
+
sampleCount: number;
|
|
95
|
+
seed?: number;
|
|
96
|
+
curveInterpolation?: string;
|
|
97
|
+
centerlineControls?: readonly PaddockTrackPoint[];
|
|
98
|
+
drsZones?: readonly PaddockDrsZone[] | null;
|
|
99
|
+
pitLane?: { enabled?: boolean } | null;
|
|
100
|
+
generationOptions?: PaddockResolvedProceduralTrackOptions;
|
|
101
|
+
[key: string]: unknown;
|
|
102
|
+
}
|
|
103
|
+
|
|
41
104
|
export interface CustomField {
|
|
42
105
|
label: string;
|
|
43
106
|
value: string;
|
|
@@ -207,6 +270,6 @@ export function normalizeSimulatorDrivers(
|
|
|
207
270
|
drivers: SimulatorDriver[],
|
|
208
271
|
options?: { entries?: ChampionshipEntryBlueprint[]; caller?: string },
|
|
209
272
|
): NormalizedSimulatorDriver[];
|
|
210
|
-
export function createProceduralTrack(seed?: number | string, options?: PaddockProceduralTrackOptions):
|
|
273
|
+
export function createProceduralTrack(seed?: number | string, options?: PaddockProceduralTrackOptions): PaddockProceduralTrack;
|
|
211
274
|
export function kphToSimSpeed(kph: number): number;
|
|
212
275
|
export function simSpeedToKph(simSpeed: number): number;
|
|
@@ -39,6 +39,69 @@ export interface PaddockProceduralTrackOptions {
|
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
export interface PaddockTrackPoint {
|
|
43
|
+
x: number;
|
|
44
|
+
y: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface PaddockDrsZone {
|
|
48
|
+
id: string;
|
|
49
|
+
startRatio: number;
|
|
50
|
+
endRatio: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface PaddockResolvedProceduralTrackOptions {
|
|
54
|
+
profile: PaddockProceduralTrackProfile;
|
|
55
|
+
length: {
|
|
56
|
+
min: number;
|
|
57
|
+
max: number;
|
|
58
|
+
};
|
|
59
|
+
startStraight: {
|
|
60
|
+
grid: number;
|
|
61
|
+
exit: number;
|
|
62
|
+
blend: number;
|
|
63
|
+
lockExtra: number;
|
|
64
|
+
};
|
|
65
|
+
pitLane: {
|
|
66
|
+
enabled: boolean;
|
|
67
|
+
};
|
|
68
|
+
shape: {
|
|
69
|
+
scale: number;
|
|
70
|
+
cornerDensity: number;
|
|
71
|
+
variation: number;
|
|
72
|
+
};
|
|
73
|
+
validation: {
|
|
74
|
+
minClearanceMultiplier: number;
|
|
75
|
+
minShapeVariation: number;
|
|
76
|
+
minNonAdjacentArcDistance: number;
|
|
77
|
+
maxLocalTurnRadians: number;
|
|
78
|
+
maxSampleHeadingDeltaRadians: number;
|
|
79
|
+
};
|
|
80
|
+
attempts: {
|
|
81
|
+
primary: number;
|
|
82
|
+
fallback: number;
|
|
83
|
+
};
|
|
84
|
+
cacheKey: string;
|
|
85
|
+
[key: string]: unknown;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface PaddockProceduralTrack {
|
|
89
|
+
name: string;
|
|
90
|
+
width: number;
|
|
91
|
+
kerbWidth: number;
|
|
92
|
+
gravelWidth: number;
|
|
93
|
+
runoffWidth: number;
|
|
94
|
+
barrierWidth: number;
|
|
95
|
+
sampleCount: number;
|
|
96
|
+
seed?: number;
|
|
97
|
+
curveInterpolation?: string;
|
|
98
|
+
centerlineControls?: readonly PaddockTrackPoint[];
|
|
99
|
+
drsZones?: readonly PaddockDrsZone[] | null;
|
|
100
|
+
pitLane?: { enabled?: boolean } | null;
|
|
101
|
+
generationOptions?: PaddockResolvedProceduralTrackOptions;
|
|
102
|
+
[key: string]: unknown;
|
|
103
|
+
}
|
|
104
|
+
|
|
42
105
|
export type PaddockStabilityState = 'stable' | 'understeer' | 'oversteer' | 'spin-risk' | 'destroyed';
|
|
43
106
|
export type PaddockPitIntent = 0 | 1 | 2;
|
|
44
107
|
export type PaddockScenarioPreset = 'cornering' | 'off-track-recovery' | 'overtaking-pack' | 'pit-entry';
|
|
@@ -674,8 +737,8 @@ export interface PaddockDriverControllerContext {
|
|
|
674
737
|
controlledDrivers: string[];
|
|
675
738
|
actionSpec: PaddockActionSpec;
|
|
676
739
|
observationSpec: PaddockObservationSpec;
|
|
677
|
-
result: PaddockEnvironmentResult
|
|
678
|
-
previousResult: PaddockEnvironmentResult |
|
|
740
|
+
result: PaddockEnvironmentResult;
|
|
741
|
+
previousResult: PaddockEnvironmentResult | null;
|
|
679
742
|
observation: Record<string, PaddockDriverObservation>;
|
|
680
743
|
orderedObservations: Array<{
|
|
681
744
|
driverId: string;
|
|
@@ -685,7 +748,7 @@ export interface PaddockDriverControllerContext {
|
|
|
685
748
|
}>;
|
|
686
749
|
metrics: Record<string, PaddockEnvironmentDriverMetrics>;
|
|
687
750
|
events: RaceEvent[];
|
|
688
|
-
info: PaddockEnvironmentResult['info'] |
|
|
751
|
+
info: PaddockEnvironmentResult['info'] | null;
|
|
689
752
|
previousActions: PaddockActionMap;
|
|
690
753
|
orderedPreviousActions: Array<PaddockAction | null>;
|
|
691
754
|
actions: PaddockActionMap | null;
|
|
@@ -721,14 +784,14 @@ export interface PaddockDriverController {
|
|
|
721
784
|
}
|
|
722
785
|
|
|
723
786
|
export interface PaddockControllerRuntime {
|
|
724
|
-
reset(options?: unknown): PaddockEnvironmentResult
|
|
725
|
-
step(actions: PaddockActionMap): PaddockEnvironmentResult
|
|
787
|
+
reset(options?: unknown): PaddockEnvironmentResult;
|
|
788
|
+
step(actions: PaddockActionMap): PaddockEnvironmentResult;
|
|
726
789
|
resetDrivers?(
|
|
727
790
|
placements: Record<string, PaddockScenarioPlacement>,
|
|
728
791
|
resultOptions?: PaddockEnvironmentResultOptions
|
|
729
|
-
): PaddockEnvironmentResult
|
|
792
|
+
): PaddockEnvironmentResult;
|
|
730
793
|
getObservation(): Record<string, PaddockDriverObservation>;
|
|
731
|
-
getState?(options?: { output?: 'full' | 'minimal' | 'none' }): PaddockEnvironmentResult['state']
|
|
794
|
+
getState?(options?: { output?: 'full' | 'minimal' | 'none' }): PaddockEnvironmentResult['state'];
|
|
732
795
|
getActionSpec(): PaddockActionSpec;
|
|
733
796
|
getObservationSpec(): PaddockObservationSpec;
|
|
734
797
|
destroy?(): void;
|
|
@@ -743,7 +806,7 @@ export interface PaddockDriverControllerLoopOptions {
|
|
|
743
806
|
}
|
|
744
807
|
|
|
745
808
|
export interface PaddockDriverControllerLoop {
|
|
746
|
-
readonly result: PaddockEnvironmentResult |
|
|
809
|
+
readonly result: PaddockEnvironmentResult | null;
|
|
747
810
|
readonly actionSpec: PaddockActionSpec | null;
|
|
748
811
|
readonly observationSpec: PaddockObservationSpec | null;
|
|
749
812
|
readonly stats: {
|
|
@@ -756,13 +819,13 @@ export interface PaddockDriverControllerLoop {
|
|
|
756
819
|
running: boolean;
|
|
757
820
|
lastError: unknown | null;
|
|
758
821
|
};
|
|
759
|
-
reset(options?: unknown): Promise<PaddockEnvironmentResult
|
|
822
|
+
reset(options?: unknown): Promise<PaddockEnvironmentResult>;
|
|
760
823
|
resetDrivers(
|
|
761
824
|
placements: Record<string, PaddockScenarioPlacement>,
|
|
762
825
|
resultOptions?: PaddockEnvironmentResultOptions
|
|
763
|
-
): Promise<PaddockEnvironmentResult
|
|
764
|
-
step(): Promise<PaddockEnvironmentResult
|
|
765
|
-
stepFrame(): Promise<PaddockEnvironmentResult
|
|
826
|
+
): Promise<PaddockEnvironmentResult>;
|
|
827
|
+
step(): Promise<PaddockEnvironmentResult>;
|
|
828
|
+
stepFrame(): Promise<PaddockEnvironmentResult>;
|
|
766
829
|
start(): void;
|
|
767
830
|
stop(): void;
|
|
768
831
|
}
|
package/src/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
PaddockActionSpec,
|
|
3
3
|
PaddockDriverControllerLoop,
|
|
4
4
|
PaddockDriverControllerLoopOptions,
|
|
5
|
+
PaddockEnvironmentResult,
|
|
5
6
|
PaddockEnvironmentOptions,
|
|
6
7
|
PaddockObservationSpec,
|
|
7
8
|
PaddockParticipantInteraction as EnvPaddockParticipantInteraction,
|
|
@@ -20,6 +21,8 @@ export type {
|
|
|
20
21
|
PaddockDriverControllerContext,
|
|
21
22
|
PaddockDriverControllerLoop,
|
|
22
23
|
PaddockDriverControllerLoopOptions,
|
|
24
|
+
PaddockEnvironmentResult,
|
|
25
|
+
PaddockEnvironmentSnapshotResult,
|
|
23
26
|
PaddockObservationSpec,
|
|
24
27
|
} from './environment/index.js';
|
|
25
28
|
|
|
@@ -71,6 +74,69 @@ export interface PaddockProceduralTrackOptions {
|
|
|
71
74
|
};
|
|
72
75
|
}
|
|
73
76
|
|
|
77
|
+
export interface PaddockTrackPoint {
|
|
78
|
+
x: number;
|
|
79
|
+
y: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface PaddockDrsZone {
|
|
83
|
+
id: string;
|
|
84
|
+
startRatio: number;
|
|
85
|
+
endRatio: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface PaddockResolvedProceduralTrackOptions {
|
|
89
|
+
profile: PaddockProceduralTrackProfile;
|
|
90
|
+
length: {
|
|
91
|
+
min: number;
|
|
92
|
+
max: number;
|
|
93
|
+
};
|
|
94
|
+
startStraight: {
|
|
95
|
+
grid: number;
|
|
96
|
+
exit: number;
|
|
97
|
+
blend: number;
|
|
98
|
+
lockExtra: number;
|
|
99
|
+
};
|
|
100
|
+
pitLane: {
|
|
101
|
+
enabled: boolean;
|
|
102
|
+
};
|
|
103
|
+
shape: {
|
|
104
|
+
scale: number;
|
|
105
|
+
cornerDensity: number;
|
|
106
|
+
variation: number;
|
|
107
|
+
};
|
|
108
|
+
validation: {
|
|
109
|
+
minClearanceMultiplier: number;
|
|
110
|
+
minShapeVariation: number;
|
|
111
|
+
minNonAdjacentArcDistance: number;
|
|
112
|
+
maxLocalTurnRadians: number;
|
|
113
|
+
maxSampleHeadingDeltaRadians: number;
|
|
114
|
+
};
|
|
115
|
+
attempts: {
|
|
116
|
+
primary: number;
|
|
117
|
+
fallback: number;
|
|
118
|
+
};
|
|
119
|
+
cacheKey: string;
|
|
120
|
+
[key: string]: unknown;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface PaddockProceduralTrack {
|
|
124
|
+
name: string;
|
|
125
|
+
width: number;
|
|
126
|
+
kerbWidth: number;
|
|
127
|
+
gravelWidth: number;
|
|
128
|
+
runoffWidth: number;
|
|
129
|
+
barrierWidth: number;
|
|
130
|
+
sampleCount: number;
|
|
131
|
+
seed?: number;
|
|
132
|
+
curveInterpolation?: string;
|
|
133
|
+
centerlineControls?: readonly PaddockTrackPoint[];
|
|
134
|
+
drsZones?: readonly PaddockDrsZone[] | null;
|
|
135
|
+
pitLane?: { enabled?: boolean } | null;
|
|
136
|
+
generationOptions?: PaddockResolvedProceduralTrackOptions;
|
|
137
|
+
[key: string]: unknown;
|
|
138
|
+
}
|
|
139
|
+
|
|
74
140
|
export type PaddockStabilityState = 'stable' | 'understeer' | 'oversteer' | 'spin-risk' | 'destroyed';
|
|
75
141
|
export type PaddockPitIntent = 0 | 1 | 2;
|
|
76
142
|
export type PaddockPitIntentRequest = PaddockPitIntent | {
|
|
@@ -959,14 +1025,14 @@ export interface PaddockEnvironmentResultOptions {
|
|
|
959
1025
|
}
|
|
960
1026
|
|
|
961
1027
|
export interface F1SimulatorExpertApi {
|
|
962
|
-
reset(options?: unknown):
|
|
963
|
-
step(actions: Record<string, F1SimulatorExpertAction>):
|
|
1028
|
+
reset(options?: unknown): PaddockEnvironmentResult;
|
|
1029
|
+
step(actions: Record<string, F1SimulatorExpertAction>): PaddockEnvironmentResult;
|
|
964
1030
|
resetDrivers?(
|
|
965
1031
|
placements: Record<string, PaddockScenarioPlacement>,
|
|
966
1032
|
resultOptions?: PaddockEnvironmentResultOptions
|
|
967
|
-
):
|
|
968
|
-
getObservation():
|
|
969
|
-
getState():
|
|
1033
|
+
): PaddockEnvironmentResult;
|
|
1034
|
+
getObservation(): PaddockEnvironmentResult['observation'];
|
|
1035
|
+
getState(): PaddockEnvironmentResult['state'];
|
|
970
1036
|
getActionSpec(): PaddockActionSpec;
|
|
971
1037
|
getObservationSpec(): PaddockObservationSpec;
|
|
972
1038
|
attachExternalRenderer(source: PaddockExternalRendererSource): void;
|
|
@@ -1133,7 +1199,7 @@ export function simSpeedToMetersPerSecond(simSpeed: number): number;
|
|
|
1133
1199
|
|
|
1134
1200
|
export function createPaddockSimulator(options: F1SimulatorOptions): PaddockSimulatorController;
|
|
1135
1201
|
export function mountF1Simulator(root: Element, options: F1SimulatorOptions): Promise<F1MountedSimulator>;
|
|
1136
|
-
export function createProceduralTrack(seed?: number | string, options?: PaddockProceduralTrackOptions):
|
|
1202
|
+
export function createProceduralTrack(seed?: number | string, options?: PaddockProceduralTrackOptions): PaddockProceduralTrack;
|
|
1137
1203
|
export function createPaddockDriverControllerLoop(options: PaddockDriverControllerLoopOptions): PaddockDriverControllerLoop;
|
|
1138
1204
|
|
|
1139
1205
|
export function mountRaceControls<T extends Element>(root: T, simulator: PaddockSimulatorController): T;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type PaddockLoadingPlaceholderVariant = 'lights' | 'custom';
|
|
2
|
+
|
|
3
|
+
export interface PaddockLoadingPlaceholderOptions {
|
|
4
|
+
label?: string;
|
|
5
|
+
detail?: string;
|
|
6
|
+
className?: string;
|
|
7
|
+
variant?: PaddockLoadingPlaceholderVariant;
|
|
8
|
+
attributes?: Record<string, string | number | boolean | null | undefined>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createPaddockLoadingPlaceholder(options?: PaddockLoadingPlaceholderOptions): string;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
function escapeHtml(value) {
|
|
2
|
+
return String(value ?? '')
|
|
3
|
+
.replaceAll('&', '&')
|
|
4
|
+
.replaceAll('<', '<')
|
|
5
|
+
.replaceAll('>', '>')
|
|
6
|
+
.replaceAll('"', '"')
|
|
7
|
+
.replaceAll("'", ''');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function normalizeVariant(value) {
|
|
11
|
+
return value === 'custom' ? 'custom' : 'lights';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function renderAttributes(attributes = {}) {
|
|
15
|
+
if (!attributes || typeof attributes !== 'object') return '';
|
|
16
|
+
return Object.entries(attributes)
|
|
17
|
+
.filter(([name, value]) => (
|
|
18
|
+
value != null &&
|
|
19
|
+
value !== false &&
|
|
20
|
+
/^[a-zA-Z_][\w:.-]*$/.test(name) &&
|
|
21
|
+
!/^on/i.test(name)
|
|
22
|
+
))
|
|
23
|
+
.map(([name, value]) => (value === true
|
|
24
|
+
? ` ${escapeHtml(name)}`
|
|
25
|
+
: ` ${escapeHtml(name)}="${escapeHtml(value)}"`))
|
|
26
|
+
.join('');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createPaddockLoadingPlaceholder({
|
|
30
|
+
label = 'Loading simulator',
|
|
31
|
+
detail = '',
|
|
32
|
+
className = '',
|
|
33
|
+
variant = 'lights',
|
|
34
|
+
attributes,
|
|
35
|
+
} = {}) {
|
|
36
|
+
const normalizedVariant = normalizeVariant(variant);
|
|
37
|
+
const extraClassName = className ? ` ${escapeHtml(className)}` : '';
|
|
38
|
+
const detailMarkup = detail
|
|
39
|
+
? `<span class="paddock-placeholder__detail">${escapeHtml(detail)}</span>`
|
|
40
|
+
: '';
|
|
41
|
+
const lightsMarkup = normalizedVariant === 'lights'
|
|
42
|
+
? `
|
|
43
|
+
<div class="paddock-placeholder__lights" aria-hidden="true">
|
|
44
|
+
<span></span>
|
|
45
|
+
<span></span>
|
|
46
|
+
<span></span>
|
|
47
|
+
<span></span>
|
|
48
|
+
<span></span>
|
|
49
|
+
</div>`
|
|
50
|
+
: '';
|
|
51
|
+
|
|
52
|
+
return `
|
|
53
|
+
<div class="paddock-placeholder${extraClassName}" data-paddock-placeholder data-paddock-placeholder-variant="${normalizedVariant}" role="status" aria-live="polite" aria-busy="true"${renderAttributes(attributes)}>
|
|
54
|
+
<div class="paddock-placeholder__content">
|
|
55
|
+
${lightsMarkup}
|
|
56
|
+
<span class="paddock-placeholder__label">${escapeHtml(label)}</span>
|
|
57
|
+
${detailMarkup}
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
`;
|
|
61
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
.paddock-placeholder {
|
|
2
|
+
--paddock-placeholder-background: #000;
|
|
3
|
+
--paddock-placeholder-color: rgba(244, 241, 234, 0.78);
|
|
4
|
+
--paddock-placeholder-light: #ff1f18;
|
|
5
|
+
--paddock-placeholder-light-dim: #8f0804;
|
|
6
|
+
--paddock-placeholder-min-height: 420px;
|
|
7
|
+
position: relative;
|
|
8
|
+
display: grid;
|
|
9
|
+
min-height: var(--paddock-placeholder-min-height);
|
|
10
|
+
place-items: center;
|
|
11
|
+
overflow: hidden;
|
|
12
|
+
color: var(--paddock-placeholder-color);
|
|
13
|
+
background: var(--paddock-placeholder-background);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.paddock-placeholder__content {
|
|
17
|
+
display: grid;
|
|
18
|
+
justify-items: center;
|
|
19
|
+
gap: 0.8rem;
|
|
20
|
+
padding: 1.25rem;
|
|
21
|
+
text-align: center;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.paddock-placeholder__label {
|
|
25
|
+
font: 800 0.72rem/1.2 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
26
|
+
letter-spacing: 0.14em;
|
|
27
|
+
text-transform: uppercase;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.paddock-placeholder__detail {
|
|
31
|
+
max-width: 32rem;
|
|
32
|
+
font: 500 0.82rem/1.4 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
33
|
+
opacity: 0.72;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.paddock-placeholder__lights {
|
|
37
|
+
display: grid;
|
|
38
|
+
grid-template-columns: repeat(5, 0.78rem);
|
|
39
|
+
gap: 0.42rem;
|
|
40
|
+
padding: 0.32rem 0.46rem;
|
|
41
|
+
border-radius: 999px;
|
|
42
|
+
background: rgba(255, 255, 255, 0.08);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.paddock-placeholder__lights span {
|
|
46
|
+
width: 0.78rem;
|
|
47
|
+
height: 0.78rem;
|
|
48
|
+
border-radius: 50%;
|
|
49
|
+
background: var(--paddock-placeholder-light-dim);
|
|
50
|
+
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08);
|
|
51
|
+
animation: paddock-placeholder-lights 1200ms cubic-bezier(0.45, 0, 0.2, 1) infinite;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.paddock-placeholder__lights span:nth-child(2) { animation-delay: 120ms; }
|
|
55
|
+
.paddock-placeholder__lights span:nth-child(3) { animation-delay: 240ms; }
|
|
56
|
+
.paddock-placeholder__lights span:nth-child(4) { animation-delay: 360ms; }
|
|
57
|
+
.paddock-placeholder__lights span:nth-child(5) { animation-delay: 480ms; }
|
|
58
|
+
|
|
59
|
+
.paddock-placeholder[data-paddock-placeholder-variant="custom"] .paddock-placeholder__content {
|
|
60
|
+
min-height: inherit;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@keyframes paddock-placeholder-lights {
|
|
64
|
+
0%, 100% {
|
|
65
|
+
background: var(--paddock-placeholder-light-dim);
|
|
66
|
+
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08);
|
|
67
|
+
}
|
|
68
|
+
45%, 70% {
|
|
69
|
+
background: var(--paddock-placeholder-light);
|
|
70
|
+
box-shadow: 0 0 14px rgba(225, 6, 0, 0.56);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getVehicleGeometryState,
|
|
3
|
-
vehicleAxes,
|
|
4
|
-
} from './vehicle/vehicleGeometry.js';
|
|
1
|
+
import { getVehicleGeometryState } from './vehicle/vehicleGeometry.js';
|
|
5
2
|
import { normalizeAngle } from './simMath.js';
|
|
6
3
|
|
|
7
4
|
const DEFAULT_SWEEP_STEPS = 16;
|
|
@@ -340,17 +337,18 @@ function writeInterpolatedBodyShape(target, geometryState, amount) {
|
|
|
340
337
|
const x = pose.previousX + (pose.x - pose.previousX) * amount;
|
|
341
338
|
const y = pose.previousY + (pose.y - pose.previousY) * amount;
|
|
342
339
|
const heading = normalizeAngle(pose.previousHeading + headingDelta * amount);
|
|
343
|
-
const
|
|
340
|
+
const cos = Math.cos(heading);
|
|
341
|
+
const sin = Math.sin(heading);
|
|
344
342
|
const halfLength = target.halfLength;
|
|
345
343
|
const halfWidth = target.halfWidth;
|
|
346
344
|
|
|
347
345
|
target.center.x = x;
|
|
348
346
|
target.center.y = y;
|
|
349
347
|
target.heading = heading;
|
|
350
|
-
target.forward.x =
|
|
351
|
-
target.forward.y =
|
|
352
|
-
target.right.x =
|
|
353
|
-
target.right.y =
|
|
348
|
+
target.forward.x = cos;
|
|
349
|
+
target.forward.y = sin;
|
|
350
|
+
target.right.x = -sin;
|
|
351
|
+
target.right.y = cos;
|
|
354
352
|
|
|
355
353
|
writeOffsetCorner(target.corners[0], target.center, target.forward, target.right, halfLength, halfWidth);
|
|
356
354
|
writeOffsetCorner(target.corners[1], target.center, target.forward, target.right, halfLength, -halfWidth);
|
|
@@ -4,7 +4,7 @@ import { decideRacingControls } from './racingControls.js';
|
|
|
4
4
|
import { decideRejoinControls } from './rejoinControls.js';
|
|
5
5
|
import { shouldContinueRejoinRecovery } from './edgeRecovery.js';
|
|
6
6
|
import { decideSafetyCarControls } from './safetyCarControls.js';
|
|
7
|
-
import { VEHICLE_LIMITS } from '../vehicle/vehiclePhysics.js';
|
|
7
|
+
import { VEHICLE_LIMITS, isSimulatorPhysicsMode } from '../vehicle/vehiclePhysics.js';
|
|
8
8
|
|
|
9
9
|
export function decideDriverControls({ car, orderIndex, race }) {
|
|
10
10
|
if (car.manualControls) return car.manualControls;
|
|
@@ -37,7 +37,7 @@ export function decideDriverControls({ car, orderIndex, race }) {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
function shouldStabilizeSimulatorCar(car, race) {
|
|
40
|
-
if (race.physicsMode
|
|
40
|
+
if (!isSimulatorPhysicsMode(race.physicsMode)) return false;
|
|
41
41
|
const gripUsage = car.gripUsage ?? 0;
|
|
42
42
|
const slipAngle = Math.abs(car.slipAngleRadians ?? 0);
|
|
43
43
|
const edgeDistance = race.track.width / 2 - VEHICLE_LIMITS.carWidth * 2.25;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { clamp, normalizeAngle } from '../simMath.js';
|
|
2
2
|
import { metersToSimUnits } from '../units.js';
|
|
3
3
|
import { pointAt } from '../track/trackModel.js';
|
|
4
|
-
import { VEHICLE_LIMITS } from '../vehicle/vehiclePhysics.js';
|
|
4
|
+
import { VEHICLE_LIMITS, isSimulatorPhysicsMode } from '../vehicle/vehiclePhysics.js';
|
|
5
5
|
import { REJOIN_LOOKAHEAD_BASE, REJOIN_LOOKAHEAD_MAX } from './driverControlConstants.js';
|
|
6
6
|
|
|
7
7
|
export function calculateTrackEdgeGuard(car, race) {
|
|
8
|
-
const simulatorMode = race.physicsMode
|
|
8
|
+
const simulatorMode = isSimulatorPhysicsMode(race.physicsMode);
|
|
9
9
|
const trackLimit = race.track.width / 2;
|
|
10
10
|
const halfCarWidth = VEHICLE_LIMITS.carWidth / 2;
|
|
11
11
|
const wholeCarOutsideLimit = trackLimit + halfCarWidth;
|