@inventure71/paddockjs 2.0.1 → 4.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 +176 -1
- package/INSTALL_AND_UPDATE.md +24 -4
- package/README.md +84 -512
- package/docs/architecture.md +38 -24
- package/docs/component_inventory.md +72 -0
- package/docs/composable-layouts.md +77 -0
- package/docs/concepts.md +2 -2
- package/docs/custom_model_controller.md +6 -6
- package/docs/data-helpers.md +50 -0
- package/docs/data_contract.md +104 -27
- package/docs/getting-started.md +75 -0
- package/docs/headless-environment.md +68 -0
- package/docs/index.md +18 -4
- package/docs/learnings.md +6 -6
- package/docs/rules.md +12 -8
- package/docs/sense_contract.md +1 -1
- package/docs/system_specs.md +71 -21
- package/docs/theming.md +64 -0
- package/docs/training.md +26 -19
- package/docs/troubleshooting.md +76 -0
- package/docs/upgrading-hosts-to-3.md +42 -0
- package/examples/python/README.md +6 -0
- package/examples/python/base_policy_server.py +19 -0
- package/package.json +10 -3
- package/src/api/PaddockSimulatorController.js +150 -24
- package/src/api/runtimeTheme.js +59 -0
- package/src/app/F1SimulatorApp.js +213 -29
- package/src/app/banners/raceDataBannerController.js +71 -36
- package/src/app/banners/stewardMessageController.js +5 -4
- package/src/app/camera/cameraConstants.js +2 -0
- package/src/app/camera/cameraController.js +86 -29
- package/src/app/camera/cameraGeometry.js +12 -1
- package/src/app/domBindings.js +89 -9
- package/src/app/layoutScheduler.js +27 -0
- package/src/app/layoutSupport.js +165 -0
- package/src/app/raceOverlayClearanceSupport.js +158 -0
- package/src/app/readouts/carOverviewRenderer.js +16 -8
- package/src/app/readouts/controlStateRenderer.js +1 -1
- package/src/app/readouts/raceStatusRenderer.js +30 -21
- package/src/app/readouts/readoutFormatters.js +2 -0
- package/src/app/readouts/telemetryRenderer.js +17 -8
- package/src/app/readouts/timingTowerRenderer.js +262 -49
- package/src/app/runtime/frameLoop.js +5 -1
- package/src/app/runtime/runtimeVisibility.js +66 -5
- package/src/config/cssValues.js +16 -0
- package/src/config/defaultOptions.js +58 -38
- package/src/config/themeOptions.js +830 -0
- package/src/config/timingGapMode.js +13 -0
- package/src/config/urlOptions.js +25 -0
- package/src/data/championship.js +14 -9
- package/src/data/index.d.ts +212 -0
- package/src/data/index.js +14 -0
- package/src/environment/controllerLoop.js +17 -3
- package/src/environment/episode.js +11 -1
- package/src/environment/index.d.ts +1 -2
- package/src/environment/metrics.js +59 -16
- package/src/environment/observationVector.js +50 -33
- package/src/environment/observations.js +89 -20
- package/src/environment/options.js +1 -1
- package/src/environment/runtime.js +44 -11
- package/src/environment/scenarios.js +1 -1
- package/src/environment/sensors/carRays.js +48 -37
- package/src/environment/sensors/index.js +501 -97
- package/src/environment/sensors/indexedRayBands.js +134 -46
- package/src/environment/sensors/rayBandTrace.js +1171 -0
- package/src/environment/sensors/rayChannels.js +108 -0
- package/src/environment/sensors/rayConfig.js +71 -25
- package/src/environment/sensors/rayGeometry.js +23 -17
- package/src/environment/sensors/sensorTargets.js +67 -1
- package/src/environment/sensors/surfaceRays.js +167 -118
- package/src/environment/sensors/trackRays.js +157 -48
- package/src/index.d.ts +153 -9
- package/src/index.js +52 -3
- package/src/simulation/collisionGeometry.js +260 -96
- package/src/simulation/driver/driverController.js +1 -1
- package/src/simulation/driver/edgeRecovery.js +1 -1
- package/src/simulation/driver/racingControls.js +2 -2
- package/src/simulation/driver/rejoinControls.js +2 -2
- package/src/simulation/pit/pitFlow.js +21 -9
- package/src/simulation/pit/pitKinematics.js +2 -2
- package/src/simulation/pit/pitQueue.js +5 -3
- package/src/simulation/pit/pitRouteMovement.js +9 -4
- package/src/simulation/pit/pitRouting.js +79 -46
- package/src/simulation/pit/pitState.js +1 -0
- package/src/simulation/pit/pitTireService.js +7 -3
- package/src/simulation/race/raceFinish.js +25 -8
- package/src/simulation/race/raceOrder.js +189 -32
- package/src/simulation/race/raceProgress.js +129 -37
- package/src/simulation/race/raceSetup.js +4 -6
- package/src/simulation/race/raceStep.js +26 -14
- package/src/simulation/race/safetyCar.js +1 -1
- package/src/simulation/race/stalledDnf.js +1 -1
- package/src/simulation/raceSimulation.js +20 -17
- package/src/simulation/rules/collisionSteward.js +40 -23
- package/src/simulation/rules/rulesReview.js +77 -56
- package/src/simulation/snapshots/raceSnapshots.js +37 -0
- package/src/simulation/timing/drsTiming.js +143 -30
- package/src/simulation/timing/gapEstimation.js +25 -3
- package/src/simulation/timing/lapTelemetry.js +138 -40
- package/src/simulation/timing/raceTiming.js +5 -1
- package/src/simulation/timing/sectorPerformance.js +104 -26
- package/src/simulation/timing/timingHistory.js +229 -14
- package/src/simulation/timing/timingLines.js +65 -7
- package/src/simulation/track/pitLaneLayout.js +30 -3
- package/src/simulation/track/pitLaneState.js +218 -19
- package/src/simulation/track/spatialQueries.js +66 -33
- package/src/simulation/track/trackMath.js +91 -24
- package/src/simulation/track/trackModel.js +2 -0
- package/src/simulation/track/trackQueryIndex.js +1468 -189
- package/src/simulation/track/trackQueryPitIndex.js +138 -14
- package/src/simulation/track/trackQueryScratch.js +7 -0
- package/src/simulation/track/trackQueryStats.js +25 -0
- package/src/simulation/track/trackSnapshotJson.js +139 -0
- package/src/simulation/track/trackStatePolicy.js +249 -2
- package/src/simulation/vehicle/contactResolution.js +99 -62
- package/src/simulation/vehicle/mainTrackWheelSurface.js +73 -34
- package/src/simulation/vehicle/pitWheelSurface.js +81 -28
- package/src/simulation/vehicle/runoffResponse.js +54 -29
- package/src/simulation/vehicle/vehicleGeometry.js +245 -51
- package/src/simulation/vehicle/vehicleKinematics.js +2 -2
- package/src/simulation/vehicle/vehiclePhysics.js +45 -30
- package/src/simulation/vehicle/vehicleSnapshots.js +35 -1
- package/src/simulation/vehicle/vehicleState.js +7 -1
- package/src/simulation/vehicle/wheelSurface.js +807 -92
- package/src/simulation/warmup/runtimeWarmup.js +0 -1
- package/src/styles.css +1568 -519
- package/src/ui/bannerTemplates.js +38 -28
- package/src/ui/cameraControlsTemplate.js +16 -5
- package/src/ui/carOverviewTemplate.js +12 -5
- package/src/ui/raceCanvasTemplate.js +31 -8
- package/src/ui/raceControlsTemplate.js +27 -10
- package/src/ui/shellTemplate.js +3 -1
- package/src/ui/telemetryDrawerTemplate.js +23 -10
- package/src/ui/telemetryTemplates.js +58 -32
- package/src/ui/templateUtils.js +48 -0
- package/src/ui/timingTowerTemplate.js +29 -12
|
@@ -49,77 +49,221 @@ export function vehicleAxes(heading) {
|
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
function writePoint(target, x, y) {
|
|
53
|
+
const point = target ?? {};
|
|
54
|
+
point.x = x;
|
|
55
|
+
point.y = y;
|
|
56
|
+
return point;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
const
|
|
59
|
+
function writePose(target, pose) {
|
|
60
|
+
const next = target ?? {};
|
|
61
|
+
next.x = pose.x;
|
|
62
|
+
next.y = pose.y;
|
|
63
|
+
next.heading = pose.heading;
|
|
64
|
+
return next;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function writeCorner(target, centerX, centerY, forwardX, forwardY, rightX, rightY, longitudinal, lateral) {
|
|
68
|
+
return writePoint(
|
|
69
|
+
target,
|
|
70
|
+
centerX + forwardX * longitudinal + rightX * lateral,
|
|
71
|
+
centerY + forwardY * longitudinal + rightY * lateral,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function writeOrientedRectValues(target, {
|
|
76
|
+
id,
|
|
77
|
+
type,
|
|
78
|
+
centerX,
|
|
79
|
+
centerY,
|
|
80
|
+
heading,
|
|
81
|
+
length,
|
|
82
|
+
width,
|
|
83
|
+
forwardX,
|
|
84
|
+
forwardY,
|
|
85
|
+
rightX,
|
|
86
|
+
rightY,
|
|
87
|
+
}, { writeCorners = true } = {}) {
|
|
88
|
+
const rect = target ?? {};
|
|
61
89
|
const halfLength = length / 2;
|
|
62
90
|
const halfWidth = width / 2;
|
|
63
|
-
const corners =
|
|
64
|
-
offsetPoint(center, axes, halfLength, halfWidth),
|
|
65
|
-
offsetPoint(center, axes, halfLength, -halfWidth),
|
|
66
|
-
offsetPoint(center, axes, -halfLength, -halfWidth),
|
|
67
|
-
offsetPoint(center, axes, -halfLength, halfWidth),
|
|
68
|
-
];
|
|
91
|
+
const corners = writeCorners || rect.corners ? (rect.corners ?? new Array(4)) : null;
|
|
69
92
|
|
|
70
|
-
|
|
93
|
+
rect.id = id;
|
|
94
|
+
rect.type = type;
|
|
95
|
+
rect.center = writePoint(rect.center, centerX, centerY);
|
|
96
|
+
rect.heading = heading;
|
|
97
|
+
rect.length = length;
|
|
98
|
+
rect.width = width;
|
|
99
|
+
rect.halfLength = halfLength;
|
|
100
|
+
rect.halfWidth = halfWidth;
|
|
101
|
+
rect.forward = writePoint(rect.forward, forwardX, forwardY);
|
|
102
|
+
rect.right = writePoint(rect.right, rightX, rightY);
|
|
103
|
+
if (corners) {
|
|
104
|
+
corners[0] = writeCorner(corners[0], centerX, centerY, forwardX, forwardY, rightX, rightY, halfLength, halfWidth);
|
|
105
|
+
corners[1] = writeCorner(corners[1], centerX, centerY, forwardX, forwardY, rightX, rightY, halfLength, -halfWidth);
|
|
106
|
+
corners[2] = writeCorner(corners[2], centerX, centerY, forwardX, forwardY, rightX, rightY, -halfLength, -halfWidth);
|
|
107
|
+
corners[3] = writeCorner(corners[3], centerX, centerY, forwardX, forwardY, rightX, rightY, -halfLength, halfWidth);
|
|
108
|
+
rect.corners = corners;
|
|
109
|
+
}
|
|
110
|
+
return rect;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function writeOrientedRect(target, { id, type, center, heading, length, width }) {
|
|
114
|
+
const cos = Math.cos(heading);
|
|
115
|
+
const sin = Math.sin(heading);
|
|
116
|
+
return writeOrientedRectValues(target, {
|
|
71
117
|
id,
|
|
72
118
|
type,
|
|
73
|
-
center,
|
|
119
|
+
centerX: center.x,
|
|
120
|
+
centerY: center.y,
|
|
74
121
|
heading,
|
|
75
122
|
length,
|
|
76
123
|
width,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
};
|
|
124
|
+
forwardX: cos,
|
|
125
|
+
forwardY: sin,
|
|
126
|
+
rightX: -sin,
|
|
127
|
+
rightY: cos,
|
|
128
|
+
});
|
|
83
129
|
}
|
|
84
130
|
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
const
|
|
131
|
+
function writeVehicleGeometry(target, pose) {
|
|
132
|
+
const geometry = target ?? {};
|
|
133
|
+
const forwardX = Math.cos(pose.heading);
|
|
134
|
+
const forwardY = Math.sin(pose.heading);
|
|
135
|
+
const rightX = -forwardY;
|
|
136
|
+
const rightY = forwardX;
|
|
137
|
+
const wheels = geometry.wheels ?? new Array(WHEEL_SPECS.length);
|
|
138
|
+
const shapes = geometry.shapes ?? new Array(1 + WHEEL_SPECS.length);
|
|
139
|
+
|
|
140
|
+
const body = writeOrientedRectValues(geometry.body, {
|
|
90
141
|
id: 'body',
|
|
91
142
|
type: 'body',
|
|
92
|
-
|
|
143
|
+
centerX: pose.x,
|
|
144
|
+
centerY: pose.y,
|
|
93
145
|
heading: pose.heading,
|
|
94
146
|
length: VEHICLE_GEOMETRY.bodyLength,
|
|
95
147
|
width: VEHICLE_GEOMETRY.bodyWidth,
|
|
148
|
+
forwardX,
|
|
149
|
+
forwardY,
|
|
150
|
+
rightX,
|
|
151
|
+
rightY,
|
|
96
152
|
});
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
spec.
|
|
103
|
-
|
|
104
|
-
|
|
153
|
+
|
|
154
|
+
for (let index = 0; index < WHEEL_SPECS.length; index += 1) {
|
|
155
|
+
const spec = WHEEL_SPECS[index];
|
|
156
|
+
const wheelCenterX =
|
|
157
|
+
pose.x +
|
|
158
|
+
forwardX * (spec.longitudinal * VEHICLE_GEOMETRY.wheelLongitudinalOffset) +
|
|
159
|
+
rightX * (spec.lateral * VEHICLE_GEOMETRY.wheelLateralOffset);
|
|
160
|
+
const wheelCenterY =
|
|
161
|
+
pose.y +
|
|
162
|
+
forwardY * (spec.longitudinal * VEHICLE_GEOMETRY.wheelLongitudinalOffset) +
|
|
163
|
+
rightY * (spec.lateral * VEHICLE_GEOMETRY.wheelLateralOffset);
|
|
164
|
+
wheels[index] = writeOrientedRectValues(wheels[index], {
|
|
105
165
|
id: spec.id,
|
|
106
166
|
type: 'wheel',
|
|
107
|
-
|
|
167
|
+
centerX: wheelCenterX,
|
|
168
|
+
centerY: wheelCenterY,
|
|
108
169
|
heading: pose.heading,
|
|
109
170
|
length: VEHICLE_GEOMETRY.wheelLength,
|
|
110
171
|
width: VEHICLE_GEOMETRY.wheelWidth,
|
|
172
|
+
forwardX,
|
|
173
|
+
forwardY,
|
|
174
|
+
rightX,
|
|
175
|
+
rightY,
|
|
111
176
|
});
|
|
112
|
-
|
|
177
|
+
shapes[index + 1] = wheels[index];
|
|
178
|
+
}
|
|
113
179
|
|
|
180
|
+
shapes[0] = body;
|
|
181
|
+
geometry.body = body;
|
|
182
|
+
geometry.wheels = wheels;
|
|
183
|
+
geometry.contactPatches = wheels;
|
|
184
|
+
geometry.shapes = shapes;
|
|
185
|
+
return geometry;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function writeCurrentVehicleGeometry(target, pose) {
|
|
189
|
+
const geometry = target ?? {};
|
|
190
|
+
const forwardX = Math.cos(pose.heading);
|
|
191
|
+
const forwardY = Math.sin(pose.heading);
|
|
192
|
+
const rightX = -forwardY;
|
|
193
|
+
const rightY = forwardX;
|
|
194
|
+
const wheels = geometry.wheels ?? new Array(WHEEL_SPECS.length);
|
|
195
|
+
|
|
196
|
+
const body = writeOrientedRectValues(geometry.body, {
|
|
197
|
+
id: 'body',
|
|
198
|
+
type: 'body',
|
|
199
|
+
centerX: pose.x,
|
|
200
|
+
centerY: pose.y,
|
|
201
|
+
heading: pose.heading,
|
|
202
|
+
length: VEHICLE_GEOMETRY.bodyLength,
|
|
203
|
+
width: VEHICLE_GEOMETRY.bodyWidth,
|
|
204
|
+
forwardX,
|
|
205
|
+
forwardY,
|
|
206
|
+
rightX,
|
|
207
|
+
rightY,
|
|
208
|
+
}, { writeCorners: false });
|
|
209
|
+
|
|
210
|
+
for (let index = 0; index < WHEEL_SPECS.length; index += 1) {
|
|
211
|
+
const spec = WHEEL_SPECS[index];
|
|
212
|
+
const wheelCenterX =
|
|
213
|
+
pose.x +
|
|
214
|
+
forwardX * (spec.longitudinal * VEHICLE_GEOMETRY.wheelLongitudinalOffset) +
|
|
215
|
+
rightX * (spec.lateral * VEHICLE_GEOMETRY.wheelLateralOffset);
|
|
216
|
+
const wheelCenterY =
|
|
217
|
+
pose.y +
|
|
218
|
+
forwardY * (spec.longitudinal * VEHICLE_GEOMETRY.wheelLongitudinalOffset) +
|
|
219
|
+
rightY * (spec.lateral * VEHICLE_GEOMETRY.wheelLateralOffset);
|
|
220
|
+
wheels[index] = writeOrientedRectValues(wheels[index], {
|
|
221
|
+
id: spec.id,
|
|
222
|
+
type: 'wheel',
|
|
223
|
+
centerX: wheelCenterX,
|
|
224
|
+
centerY: wheelCenterY,
|
|
225
|
+
heading: pose.heading,
|
|
226
|
+
length: VEHICLE_GEOMETRY.wheelLength,
|
|
227
|
+
width: VEHICLE_GEOMETRY.wheelWidth,
|
|
228
|
+
forwardX,
|
|
229
|
+
forwardY,
|
|
230
|
+
rightX,
|
|
231
|
+
rightY,
|
|
232
|
+
}, { writeCorners: false });
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
geometry.body = body;
|
|
236
|
+
geometry.wheels = wheels;
|
|
237
|
+
geometry.contactPatches = wheels;
|
|
238
|
+
return geometry;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function createOrientedRect({ id, type, center, heading, length, width }) {
|
|
242
|
+
return writeOrientedRect(null, { id, type, center, heading, length, width });
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function createVehicleGeometry(car, options = {}) {
|
|
246
|
+
const pose = vehiclePose(car, options);
|
|
247
|
+
return writeVehicleGeometry(null, pose);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function currentPoseSignature(pose) {
|
|
251
|
+
return [
|
|
252
|
+
pose.x,
|
|
253
|
+
pose.y,
|
|
254
|
+
pose.heading,
|
|
255
|
+
].join(':');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function currentPoseState(car) {
|
|
114
259
|
return {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
shapes: [body, ...wheels],
|
|
260
|
+
x: finiteOr(car.x, 0),
|
|
261
|
+
y: finiteOr(car.y, 0),
|
|
262
|
+
heading: normalizeAngle(finiteOr(car.heading, 0)),
|
|
119
263
|
};
|
|
120
264
|
}
|
|
121
265
|
|
|
122
|
-
function
|
|
266
|
+
function sweptPoseSignature(pose) {
|
|
123
267
|
return [
|
|
124
268
|
pose.x,
|
|
125
269
|
pose.y,
|
|
@@ -131,9 +275,7 @@ function geometryPoseSignature(pose) {
|
|
|
131
275
|
}
|
|
132
276
|
|
|
133
277
|
function geometryPose(car) {
|
|
134
|
-
const x =
|
|
135
|
-
const y = finiteOr(car.y, 0);
|
|
136
|
-
const heading = normalizeAngle(finiteOr(car.heading, 0));
|
|
278
|
+
const { x, y, heading } = currentPoseState(car);
|
|
137
279
|
return {
|
|
138
280
|
x,
|
|
139
281
|
y,
|
|
@@ -144,12 +286,21 @@ function geometryPose(car) {
|
|
|
144
286
|
};
|
|
145
287
|
}
|
|
146
288
|
|
|
289
|
+
function currentGeometryStateMatches(car, state) {
|
|
290
|
+
const pose = state?.pose;
|
|
291
|
+
if (!pose) return false;
|
|
292
|
+
const currentPose = currentPoseState(car);
|
|
293
|
+
return (
|
|
294
|
+
pose.x === currentPose.x &&
|
|
295
|
+
pose.y === currentPose.y &&
|
|
296
|
+
pose.heading === currentPose.heading
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
147
300
|
function geometryStateMatches(car, state) {
|
|
148
301
|
const pose = state?.pose;
|
|
149
302
|
if (!pose) return false;
|
|
150
|
-
const x =
|
|
151
|
-
const y = finiteOr(car.y, 0);
|
|
152
|
-
const heading = normalizeAngle(finiteOr(car.heading, 0));
|
|
303
|
+
const { x, y, heading } = currentPoseState(car);
|
|
153
304
|
return (
|
|
154
305
|
pose.x === x &&
|
|
155
306
|
pose.y === y &&
|
|
@@ -197,15 +348,21 @@ export function mergeAabbs(aabbs) {
|
|
|
197
348
|
|
|
198
349
|
export function createVehicleGeometryState(car) {
|
|
199
350
|
const pose = geometryPose(car);
|
|
200
|
-
const current =
|
|
201
|
-
const previous =
|
|
351
|
+
const current = writeVehicleGeometry(null, pose);
|
|
352
|
+
const previous = writeVehicleGeometry(null, {
|
|
353
|
+
x: pose.previousX,
|
|
354
|
+
y: pose.previousY,
|
|
355
|
+
heading: pose.previousHeading,
|
|
356
|
+
});
|
|
202
357
|
const bodyAabb = createVehicleShapeAabb(current.body);
|
|
203
358
|
const previousBodyAabb = createVehicleShapeAabb(previous.body);
|
|
204
359
|
const sweptBodyAabb = mergeAabbs([previousBodyAabb, bodyAabb]);
|
|
205
360
|
|
|
206
361
|
return {
|
|
207
362
|
pose,
|
|
208
|
-
|
|
363
|
+
currentSignature: currentPoseSignature(pose),
|
|
364
|
+
sweptSignature: sweptPoseSignature(pose),
|
|
365
|
+
signature: sweptPoseSignature(pose),
|
|
209
366
|
current,
|
|
210
367
|
previous,
|
|
211
368
|
body: current.body,
|
|
@@ -217,12 +374,49 @@ export function createVehicleGeometryState(car) {
|
|
|
217
374
|
};
|
|
218
375
|
}
|
|
219
376
|
|
|
377
|
+
export function createCurrentVehicleGeometryState(car, target = null) {
|
|
378
|
+
const pose = currentPoseState(car);
|
|
379
|
+
const current = writeCurrentVehicleGeometry(target?.current ?? null, pose);
|
|
380
|
+
const state = target ?? {};
|
|
381
|
+
state.pose = writePose(state.pose, pose);
|
|
382
|
+
state.current = current;
|
|
383
|
+
state.body = current.body;
|
|
384
|
+
state.wheels = current.wheels;
|
|
385
|
+
state.contactPatches = current.contactPatches;
|
|
386
|
+
return state;
|
|
387
|
+
}
|
|
388
|
+
|
|
220
389
|
export function getVehicleGeometryState(car) {
|
|
221
390
|
if (geometryStateMatches(car, car.geometryState)) return car.geometryState;
|
|
222
391
|
car.geometryState = createVehicleGeometryState(car);
|
|
223
392
|
return car.geometryState;
|
|
224
393
|
}
|
|
225
394
|
|
|
395
|
+
export function getCurrentVehicleGeometryState(car) {
|
|
396
|
+
if (currentGeometryStateMatches(car, car.currentGeometryState)) return car.currentGeometryState;
|
|
397
|
+
if (currentGeometryStateMatches(car, car.geometryState)) return car.geometryState;
|
|
398
|
+
car.currentGeometryState = createCurrentVehicleGeometryState(car, car.currentGeometryState ?? null);
|
|
399
|
+
return car.currentGeometryState;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export function ensureOrientedRectCorners(rect) {
|
|
403
|
+
if (!rect) return [];
|
|
404
|
+
if (rect.corners) return rect.corners;
|
|
405
|
+
return writeOrientedRectValues(rect, {
|
|
406
|
+
id: rect.id ?? null,
|
|
407
|
+
type: rect.type ?? null,
|
|
408
|
+
centerX: rect.center?.x ?? 0,
|
|
409
|
+
centerY: rect.center?.y ?? 0,
|
|
410
|
+
heading: rect.heading ?? 0,
|
|
411
|
+
length: rect.length ?? 0,
|
|
412
|
+
width: rect.width ?? 0,
|
|
413
|
+
forwardX: rect.forward?.x ?? Math.cos(rect.heading ?? 0),
|
|
414
|
+
forwardY: rect.forward?.y ?? Math.sin(rect.heading ?? 0),
|
|
415
|
+
rightX: rect.right?.x ?? -Math.sin(rect.heading ?? 0),
|
|
416
|
+
rightY: rect.right?.y ?? Math.cos(rect.heading ?? 0),
|
|
417
|
+
}).corners;
|
|
418
|
+
}
|
|
419
|
+
|
|
226
420
|
export function createVehicleAabb(car, options = {}) {
|
|
227
421
|
const geometry = createVehicleGeometry(car, options);
|
|
228
422
|
return mergeAabbs(geometry.shapes.map(createVehicleShapeAabb));
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export function freezeVehicleMotion(car, {
|
|
2
2
|
brake = 1,
|
|
3
3
|
clearManualControls = false,
|
|
4
|
-
physicsMode = '
|
|
4
|
+
physicsMode = 'arcade',
|
|
5
5
|
stabilityState = car.stabilityState ?? 'stable',
|
|
6
6
|
} = {}) {
|
|
7
7
|
car.speed = 0;
|
|
8
|
-
if (physicsMode === '
|
|
8
|
+
if (physicsMode === 'advanced') {
|
|
9
9
|
car.velocityX = 0;
|
|
10
10
|
car.velocityY = 0;
|
|
11
11
|
} else {
|
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
simUnitsToMeters,
|
|
8
8
|
metersToSimUnits,
|
|
9
9
|
} from '../units.js';
|
|
10
|
-
import { VEHICLE_GEOMETRY, getCarCorners
|
|
10
|
+
import { VEHICLE_GEOMETRY, getCarCorners } from './vehicleGeometry.js';
|
|
11
11
|
|
|
12
12
|
const G = 9.80665;
|
|
13
13
|
|
|
14
|
-
export const PHYSICS_MODES = ['arcade', '
|
|
14
|
+
export const PHYSICS_MODES = ['arcade', 'advanced'];
|
|
15
15
|
export const DEFAULT_PHYSICS_MODE = 'arcade';
|
|
16
16
|
|
|
17
17
|
export const VEHICLE_LIMITS = {
|
|
@@ -50,7 +50,7 @@ const SIMULATOR_SURFACE_MODEL = {
|
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
export function normalizePhysicsMode(value) {
|
|
53
|
-
return value === '
|
|
53
|
+
return value === 'advanced' ? 'advanced' : DEFAULT_PHYSICS_MODE;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function accelerationLimit(speed, surfaceGrip) {
|
|
@@ -73,17 +73,26 @@ function surfaceResistance(surfaceName, model = SURFACE_MODEL) {
|
|
|
73
73
|
return surface.drag * 0.14 + surface.rollingResistance * 2.4 + (1 - surface.grip) * 0.65;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
function sideWheelResistance(wheels, side, model = SURFACE_MODEL) {
|
|
77
|
-
const sideWheels = wheels.filter((wheel) => wheel.id?.endsWith(`-${side}`));
|
|
78
|
-
if (!sideWheels.length) return 0;
|
|
79
|
-
return sideWheels.reduce((total, wheel) => total + surfaceResistance(wheel.surface, model), 0) / sideWheels.length;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
76
|
function wheelDragYawRate(car, model = SURFACE_MODEL) {
|
|
83
77
|
const wheels = Array.isArray(car.wheelStates) ? car.wheelStates : [];
|
|
84
78
|
if (!wheels.length) return 0;
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
let leftResistanceTotal = 0;
|
|
80
|
+
let rightResistanceTotal = 0;
|
|
81
|
+
let leftCount = 0;
|
|
82
|
+
let rightCount = 0;
|
|
83
|
+
for (let index = 0; index < wheels.length; index += 1) {
|
|
84
|
+
const wheel = wheels[index];
|
|
85
|
+
const resistance = surfaceResistance(wheel.surface, model);
|
|
86
|
+
if (wheel.id?.endsWith('-left')) {
|
|
87
|
+
leftResistanceTotal += resistance;
|
|
88
|
+
leftCount += 1;
|
|
89
|
+
} else if (wheel.id?.endsWith('-right')) {
|
|
90
|
+
rightResistanceTotal += resistance;
|
|
91
|
+
rightCount += 1;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const leftResistance = leftCount > 0 ? leftResistanceTotal / leftCount : 0;
|
|
95
|
+
const rightResistance = rightCount > 0 ? rightResistanceTotal / rightCount : 0;
|
|
87
96
|
const speedFactor = clamp(car.speed / VEHICLE_LIMITS.maxSpeed, 0, 1);
|
|
88
97
|
return clamp(
|
|
89
98
|
(rightResistance - leftResistance) * speedFactor * WHEEL_DRAG_YAW_GAIN,
|
|
@@ -111,7 +120,7 @@ function setPhysicsTelemetry(car, {
|
|
|
111
120
|
}
|
|
112
121
|
|
|
113
122
|
export function integrateVehiclePhysics(car, controls, dt, options = {}) {
|
|
114
|
-
if (normalizePhysicsMode(options.physicsMode) === '
|
|
123
|
+
if (normalizePhysicsMode(options.physicsMode) === 'advanced') {
|
|
115
124
|
return integrateSimulatorVehiclePhysics(car, controls, dt, options);
|
|
116
125
|
}
|
|
117
126
|
return integrateArcadeVehiclePhysics(car, controls, dt, options);
|
|
@@ -194,20 +203,22 @@ function simulatorSurface(surfaceName) {
|
|
|
194
203
|
function aggregateSimulatorSurface(car) {
|
|
195
204
|
const wheels = Array.isArray(car.wheelStates) ? car.wheelStates : [];
|
|
196
205
|
if (!wheels.length) return simulatorSurface(car.trackState?.surface ?? 'track');
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
+
let grip = 0;
|
|
207
|
+
let drag = 0;
|
|
208
|
+
let rollingResistance = 0;
|
|
209
|
+
let scrub = 0;
|
|
210
|
+
for (let index = 0; index < wheels.length; index += 1) {
|
|
211
|
+
const surface = simulatorSurface(wheels[index].surface);
|
|
212
|
+
grip += surface.grip;
|
|
213
|
+
drag += surface.drag;
|
|
214
|
+
rollingResistance += surface.rollingResistance;
|
|
215
|
+
scrub += surface.scrub;
|
|
216
|
+
}
|
|
206
217
|
return {
|
|
207
|
-
grip:
|
|
208
|
-
drag:
|
|
209
|
-
rollingResistance:
|
|
210
|
-
scrub:
|
|
218
|
+
grip: grip / wheels.length,
|
|
219
|
+
drag: drag / wheels.length,
|
|
220
|
+
rollingResistance: rollingResistance / wheels.length,
|
|
221
|
+
scrub: scrub / wheels.length,
|
|
211
222
|
};
|
|
212
223
|
}
|
|
213
224
|
|
|
@@ -256,9 +267,13 @@ function integrateSimulatorVehiclePhysics(car, controls, dt, options = {}) {
|
|
|
256
267
|
const speedRatio = clamp(speedBefore / VEHICLE_LIMITS.maxSpeed, 0, 1);
|
|
257
268
|
const downforceGrip = car.downforceCoefficient * speedBeforeMps * speedBeforeMps / car.mass;
|
|
258
269
|
const totalGripAcceleration = Math.max(0.1, (car.tireGrip * tireFactor * G + downforceGrip) * surfaceGrip);
|
|
259
|
-
const
|
|
260
|
-
const
|
|
261
|
-
const
|
|
270
|
+
const heading = car.heading ?? 0;
|
|
271
|
+
const forwardX = Math.cos(heading);
|
|
272
|
+
const forwardY = Math.sin(heading);
|
|
273
|
+
const rightX = -forwardY;
|
|
274
|
+
const rightY = forwardX;
|
|
275
|
+
const forwardVelocity = car.velocityX * forwardX + car.velocityY * forwardY;
|
|
276
|
+
const lateralVelocity = car.velocityX * rightX + car.velocityY * rightY;
|
|
262
277
|
const forwardSpeedMps = simUnitsToMeters(forwardVelocity);
|
|
263
278
|
const lateralSpeedMps = simUnitsToMeters(lateralVelocity);
|
|
264
279
|
const currentSlipAngle = Math.atan2(lateralSpeedMps, Math.max(Math.abs(forwardSpeedMps), 0.1));
|
|
@@ -328,8 +343,8 @@ function integrateSimulatorVehiclePhysics(car, controls, dt, options = {}) {
|
|
|
328
343
|
car.wheelDragYawRate = requestedWheelDragYawRate;
|
|
329
344
|
car.heading = normalizeAngle(car.heading + car.yawRate * dt);
|
|
330
345
|
|
|
331
|
-
const accelerationX =
|
|
332
|
-
const accelerationY =
|
|
346
|
+
const accelerationX = forwardX * longitudinalAcceleration + rightX * actualLateralAcceleration;
|
|
347
|
+
const accelerationY = forwardY * longitudinalAcceleration + rightY * actualLateralAcceleration;
|
|
333
348
|
car.velocityX += metersToSimUnits(accelerationX) * dt;
|
|
334
349
|
car.velocityY += metersToSimUnits(accelerationY) * dt;
|
|
335
350
|
car.speed = limitSimulatorSpeed(car);
|
|
@@ -17,7 +17,7 @@ function serializeAppliedControls(car, dependencies) {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
function serializeVelocityComponent(car, component, dependencies) {
|
|
20
|
-
if (dependencies.physicsMode !== '
|
|
20
|
+
if (dependencies.physicsMode !== 'advanced') return null;
|
|
21
21
|
return finiteOrNull(car[component]);
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -141,6 +141,17 @@ export function serializeCar(car, rank, penaltySeconds = 0, dependencies) {
|
|
|
141
141
|
signedOffset: car.trackState?.signedOffset ?? 0,
|
|
142
142
|
crossTrackError: car.trackState?.crossTrackError ?? 0,
|
|
143
143
|
surface: car.trackState?.surface ?? 'track',
|
|
144
|
+
trackState: car.trackState ? {
|
|
145
|
+
distance: car.trackState.distance,
|
|
146
|
+
signedOffset: car.trackState.signedOffset,
|
|
147
|
+
crossTrackError: car.trackState.crossTrackError,
|
|
148
|
+
surface: car.trackState.surface,
|
|
149
|
+
inPitLane: Boolean(car.trackState.inPitLane),
|
|
150
|
+
pitLanePart: car.trackState.pitLanePart ?? null,
|
|
151
|
+
pitBoxId: car.trackState.pitBoxId ?? null,
|
|
152
|
+
curvature: car.trackState.curvature ?? 0,
|
|
153
|
+
heading: car.trackState.heading,
|
|
154
|
+
} : null,
|
|
144
155
|
wheels: serializeWheels(car.wheelStates),
|
|
145
156
|
inPitLane: Boolean(car.trackState?.inPitLane),
|
|
146
157
|
pitLanePart: car.trackState?.pitLanePart ?? null,
|
|
@@ -155,6 +166,29 @@ export function serializeCar(car, rank, penaltySeconds = 0, dependencies) {
|
|
|
155
166
|
};
|
|
156
167
|
}
|
|
157
168
|
|
|
169
|
+
export function serializeRenderCarInto(target, car, dependencies) {
|
|
170
|
+
const { serializeRenderPitStop } = dependencies;
|
|
171
|
+
target.id = car.id;
|
|
172
|
+
target.color = car.color;
|
|
173
|
+
target.previousX = car.previousX ?? car.x;
|
|
174
|
+
target.previousY = car.previousY ?? car.y;
|
|
175
|
+
target.x = car.x;
|
|
176
|
+
target.y = car.y;
|
|
177
|
+
target.previousHeading = car.previousHeading ?? car.heading;
|
|
178
|
+
target.heading = car.heading;
|
|
179
|
+
target.drsActive = Boolean(car.drsActive);
|
|
180
|
+
target.destroyed = Boolean(car.destroyed);
|
|
181
|
+
target.destroyReason = car.destroyReason ?? null;
|
|
182
|
+
target.dnf = Boolean(car.destroyed || car.outOfRace);
|
|
183
|
+
target.dnfReason = car.dnfReason ?? car.destroyReason ?? null;
|
|
184
|
+
target.dnfAt = car.dnfAt ?? car.destroyedAt ?? null;
|
|
185
|
+
target.dnfOrder = car.dnfOrder ?? null;
|
|
186
|
+
target.outOfRace = Boolean(car.outOfRace || car.destroyed);
|
|
187
|
+
target.pitStop = serializeRenderPitStop(car.pitStop);
|
|
188
|
+
target.interaction = serializeParticipantInteraction(car.interaction);
|
|
189
|
+
return target;
|
|
190
|
+
}
|
|
191
|
+
|
|
158
192
|
export function serializeRenderCar(car, dependencies) {
|
|
159
193
|
const { serializeRenderPitStop } = dependencies;
|
|
160
194
|
return {
|
|
@@ -96,6 +96,8 @@ export function createCar(driver, index, random, track, { standingStart = false,
|
|
|
96
96
|
drsActive: false,
|
|
97
97
|
drsZoneId: null,
|
|
98
98
|
drsZoneEnabled: false,
|
|
99
|
+
_drsZoneRef: null,
|
|
100
|
+
_drsNextZoneIndex: null,
|
|
99
101
|
timingHistory: [],
|
|
100
102
|
lapTelemetry: createLapTelemetry?.(track, 0, gridDistance) ?? null,
|
|
101
103
|
drsDetection: {},
|
|
@@ -113,6 +115,8 @@ export function clearCarDrsState(car) {
|
|
|
113
115
|
car.drsZoneEnabled = false;
|
|
114
116
|
car.drsActive = false;
|
|
115
117
|
car.drsEligible = false;
|
|
118
|
+
car._drsZoneRef = null;
|
|
119
|
+
car._drsNextZoneIndex = null;
|
|
116
120
|
car.drsDetection = {};
|
|
117
121
|
}
|
|
118
122
|
|
|
@@ -158,13 +162,15 @@ export function applyExternalCarState(car, partial, context) {
|
|
|
158
162
|
nextPartial.raceDistance != null
|
|
159
163
|
) {
|
|
160
164
|
car.gridLocked = false;
|
|
165
|
+
car._drsZoneRef = null;
|
|
166
|
+
car._drsNextZoneIndex = null;
|
|
161
167
|
if (raceControl.mode === 'pre-start' && cars.every((item) => !item.gridLocked)) {
|
|
162
168
|
releaseRaceStart();
|
|
163
169
|
}
|
|
164
170
|
}
|
|
165
171
|
car.speed = clamp(Number.isFinite(car.speed) ? car.speed : 0, 0, VEHICLE_LIMITS.maxSpeed);
|
|
166
172
|
car.heading = normalizeAngle(Number.isFinite(car.heading) ? car.heading : 0);
|
|
167
|
-
syncExternalVelocityState(car, nextPartial, physicsMode === '
|
|
173
|
+
syncExternalVelocityState(car, nextPartial, physicsMode === 'advanced');
|
|
168
174
|
const centerState = nearestTrackStateForCar(track, car, car, car.progress ?? car.raceDistance);
|
|
169
175
|
applyWheelSurfaceState(car, track, { centerState });
|
|
170
176
|
if (
|