@inventure71/paddockjs 4.1.0 → 4.3.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 +76 -0
- package/INSTALL_AND_UPDATE.md +31 -5
- package/README.md +34 -3
- package/docs/advanced_ai_pace.md +131 -0
- package/docs/advanced_physics_rebuild.md +166 -0
- package/docs/architecture.md +111 -30
- package/docs/cleanup_coverage.md +284 -0
- package/docs/component_inventory.md +32 -16
- package/docs/composable-layouts.md +2 -0
- package/docs/concepts.md +12 -1
- package/docs/custom_model_controller.md +1 -1
- package/docs/data_contract.md +33 -10
- package/docs/data_model.md +4 -2
- package/docs/efficiency_cleanup_2026-10-13.md +80 -0
- package/docs/getting-started.md +3 -0
- package/docs/grid_walk.md +80 -0
- package/docs/index.md +5 -2
- package/docs/keyboard_driving_fix.md +118 -0
- package/docs/limit_driving_2026-10-14.md +88 -0
- package/docs/loading.md +141 -0
- package/docs/maintainability_audit_2026-09-08.md +146 -0
- package/docs/racing_ai_improvements.md +145 -0
- package/docs/rules.md +43 -13
- package/docs/sense_contract.md +4 -1
- package/docs/system_specs.md +40 -12
- package/docs/testing-reliability-guide.md +67 -0
- package/docs/v11_improvements.md +108 -0
- package/package.json +23 -15
- package/src/api/MountedSimulatorComponents.js +101 -0
- package/src/api/PaddockSimulatorController.js +56 -81
- package/src/app/BrowserExpertAdapter.js +0 -6
- package/src/app/BrowserExpertExternalRenderer.js +72 -50
- package/src/app/ExternalTrackSnapshot.js +50 -0
- package/src/app/F1SimulatorApp.js +242 -297
- package/src/app/HostLifecycleEmitter.js +93 -0
- package/src/app/banners/raceDataBannerController.js +9 -4
- package/src/app/camera/cameraConstants.js +1 -1
- package/src/app/camera/cameraController.js +10 -13
- package/src/app/camera/cameraGeometry.js +1 -1
- package/src/app/controls/SimulatorViewControls.js +45 -0
- package/src/app/controls/workbenchBindings.js +64 -0
- package/src/app/controls/workbenchDisclosure.js +41 -0
- package/src/app/domBindings.js +21 -13
- package/src/app/gridWalk/GridWalkController.js +180 -0
- package/src/app/gridWalk/gridWalkCamera.js +88 -0
- package/src/app/gridWalk/gridWalkControls.js +24 -0
- package/src/app/gridWalk/gridWalkCrew.js +246 -0
- package/src/app/gridWalk/gridWalkLabels.js +104 -0
- package/src/app/gridWalk/gridWalkLayout.js +46 -0
- package/src/app/gridWalk/gridWalkNavigation.js +99 -0
- package/src/app/gridWalk/gridWalkProjectBanner.js +43 -0
- package/src/app/gridWalk/gridWalkSession.js +50 -0
- package/src/app/layoutSupport.js +2 -0
- package/src/app/raceOverlayClearanceSupport.js +21 -7
- package/src/app/readouts/carOverviewRenderer.js +9 -4
- package/src/app/readouts/readoutFormatters.js +1 -8
- package/src/app/readouts/telemetryRenderer.js +1 -3
- package/src/app/readouts/timingTowerRenderer.js +48 -50
- package/src/app/rendering/appAssets.js +8 -10
- package/src/app/rendering/carRenderer.js +27 -79
- package/src/app/rendering/displayUtils.js +0 -6
- package/src/app/rendering/expertSensorRenderer.js +1 -1
- package/src/app/rendering/replayGhostRenderer.js +4 -1
- package/src/app/rendering/trackRenderer.js +2 -2
- package/src/app/runtime/frameLoop.js +18 -17
- package/src/app/runtime/pixiStartupLifecycle.js +94 -0
- package/src/app/runtime/runtimeTiming.js +4 -1
- package/src/app/runtime/runtimeVisibility.js +7 -2
- package/src/config/cssValues.js +1 -1
- package/src/config/defaultOptions.js +12 -173
- package/src/config/restartOptions.js +1 -0
- package/src/config/runtimeSeed.js +10 -0
- package/src/config/simulatorDefaults.js +106 -0
- package/src/config/telemetryModules.js +20 -0
- package/src/config/theme/cssTokens.js +85 -0
- package/src/config/theme/themeCss.js +72 -0
- package/src/config/theme/themeInputs.js +155 -0
- package/src/config/theme/themeResolver.js +172 -0
- package/src/config/theme/themeSelectors.js +69 -0
- package/src/config/theme/themeTokens.js +257 -0
- package/src/config/theme/themeUtils.js +15 -0
- package/src/config/themeOptions.js +14 -830
- package/src/config/timingGapMode.js +0 -2
- package/src/config/uiOptions.js +53 -0
- package/src/data/championship.js +16 -158
- package/src/data/championshipDefaults.js +87 -0
- package/src/data/driverData.js +10 -20
- package/src/data/entryIdentity.js +67 -0
- package/src/data/entryPresentation.js +23 -0
- package/src/data/entryTypes.d.ts +160 -0
- package/src/data/index.d.ts +12 -195
- package/src/data/ratingScale.js +14 -0
- package/src/data/teamTypes.d.ts +23 -0
- package/src/data/trackTypes.d.ts +101 -0
- package/src/data/vehicleData.js +12 -23
- package/src/environment/actions.js +3 -2
- package/src/environment/controllerLoop.js +57 -44
- package/src/environment/controllerScheduler.js +30 -0
- package/src/environment/driverObservations.js +260 -0
- package/src/environment/driverReset.js +51 -0
- package/src/environment/episode.js +0 -1
- package/src/environment/evaluation.js +6 -90
- package/src/environment/evaluationTracker.js +100 -0
- package/src/environment/events.js +23 -0
- package/src/environment/index.d.ts +35 -91
- package/src/environment/metrics.js +3 -17
- package/src/environment/observationSchema.js +104 -0
- package/src/environment/observationVector.js +16 -115
- package/src/environment/observations.js +16 -316
- package/src/environment/options.js +5 -24
- package/src/environment/renderFrames.js +21 -0
- package/src/environment/resetOptions.js +41 -0
- package/src/environment/results.js +125 -0
- package/src/environment/rewards.js +10 -16
- package/src/environment/runtime.js +17 -300
- package/src/environment/scenarioOptions.js +90 -0
- package/src/environment/scenarioPlacements.js +70 -0
- package/src/environment/scenarioPresets.js +78 -0
- package/src/environment/scenarios.js +5 -236
- package/src/environment/scratch.js +18 -0
- package/src/environment/sensorOptions.js +20 -0
- package/src/environment/sensors/index.js +2 -556
- package/src/environment/sensors/indexedRayBands.js +0 -18
- package/src/environment/sensors/nearbyCars.js +18 -15
- package/src/environment/sensors/opponentRadar.js +3 -2
- package/src/environment/sensors/pitConnectorProximity.js +20 -0
- package/src/environment/sensors/rayBandTrace.js +1 -37
- package/src/environment/sensors/rayBatch.js +81 -0
- package/src/environment/sensors/rayConfig.js +26 -0
- package/src/environment/sensors/rayGeometry.js +0 -8
- package/src/environment/sensors/rayGuards.js +0 -6
- package/src/environment/sensors/rayOutputs.js +75 -0
- package/src/environment/sensors/rayQuery.js +110 -0
- package/src/environment/sensors/rayScratch.js +146 -0
- package/src/environment/sensors/sensorTargets.js +12 -4
- package/src/environment/sensors/surfaceRays.js +6 -61
- package/src/environment/sensors/trackRays.js +7 -80
- package/src/environment/simulationSetup.js +30 -0
- package/src/environment/specs.js +15 -109
- package/src/index.d.ts +43 -202
- package/src/index.js +28 -6
- package/src/placeholder/index.js +1 -8
- package/src/rendering/displayLifecycle.js +6 -0
- package/src/rendering/proceduralTrackAsset.js +1 -1
- package/src/rendering/renderSnapshot.js +3 -0
- package/src/rendering/track/finishLineRenderer.js +1 -1
- package/src/rendering/track/gridRenderer.js +1 -1
- package/src/rendering/track/offsetStrokeSafety.js +2 -2
- package/src/rendering/track/pitLaneRenderer.js +1 -1
- package/src/rendering/track/trackMaterialRenderer.js +4 -4
- package/src/rendering/track/trackRenderConstants.js +0 -1
- package/src/rendering/track/trackRenderGeometry.js +1 -7
- package/src/simulation/collisionGeometry.js +12 -20
- package/src/simulation/driver/advancedPathControls.js +53 -0
- package/src/simulation/driver/advancedRacePace.js +112 -0
- package/src/simulation/driver/advancedRacingControls.js +61 -0
- package/src/simulation/driver/advancedRacingEnvelope.js +255 -0
- package/src/simulation/driver/advancedRacingSteering.js +76 -0
- package/src/simulation/driver/advancedRacingTiming.js +16 -0
- package/src/simulation/driver/arcadeRacePace.js +83 -0
- package/src/simulation/driver/arcadeRacingControls.js +111 -0
- package/src/simulation/driver/arcadeRacingEnvelope.js +63 -0
- package/src/simulation/driver/driverControlConstants.js +0 -2
- package/src/simulation/driver/driverController.js +16 -5
- package/src/simulation/driver/edgeRecovery.js +2 -3
- package/src/simulation/driver/racingControls.js +10 -396
- package/src/simulation/driver/racingDriverProfile.js +48 -0
- package/src/simulation/driver/racingLinePlan.js +54 -20
- package/src/simulation/driver/rejoinControls.js +59 -112
- package/src/simulation/driver/safetyCarControls.js +12 -0
- package/src/simulation/driver/trafficScan.js +136 -27
- package/src/simulation/participants/participantInteractions.js +24 -15
- package/src/simulation/pit/pitCarPlacement.js +29 -0
- package/src/simulation/pit/pitFlow.js +8 -30
- package/src/simulation/pit/pitIntent.js +4 -3
- package/src/simulation/pit/pitOccupancy.js +2 -2
- package/src/simulation/pit/pitQueue.js +2 -18
- package/src/simulation/pit/pitRouteMovement.js +1 -36
- package/src/simulation/pit/pitRouting.js +5 -81
- package/src/simulation/pit/pitService.js +3 -32
- package/src/simulation/pit/pitServiceConstants.js +1 -0
- package/src/simulation/pit/pitServiceProgress.js +34 -0
- package/src/simulation/pit/pitSnapshots.js +8 -4
- package/src/simulation/pit/pitState.js +1 -5
- package/src/simulation/race/classification.js +1 -8
- package/src/simulation/race/raceFinish.js +3 -8
- package/src/simulation/race/raceOrder.js +2 -13
- package/src/simulation/race/raceProgress.js +6 -20
- package/src/simulation/race/raceSetup.js +1 -1
- package/src/simulation/race/raceStep.js +20 -11
- package/src/simulation/race/redFlag.js +2 -4
- package/src/simulation/race/safetyCar.js +5 -9
- package/src/simulation/race/stalledDnf.js +2 -4
- package/src/simulation/raceSimulation.js +6 -38
- package/src/simulation/replay/replayGhosts.js +44 -32
- package/src/simulation/rules/collisionSteward.js +0 -14
- package/src/simulation/rules/penaltyActions.js +37 -0
- package/src/simulation/rules/penaltyLedger.js +8 -1
- package/src/simulation/rules/pitLaneSpeedingSteward.js +1 -1
- package/src/simulation/rules/rulesReview.js +20 -16
- package/src/simulation/rules/trackLimitsSteward.js +2 -2
- package/src/simulation/simMath.js +4 -0
- package/src/simulation/snapshots/raceSnapshots.js +30 -11
- package/src/simulation/timing/drsState.js +8 -0
- package/src/simulation/timing/drsTiming.js +2 -4
- package/src/simulation/timing/lapTelemetry.js +7 -4
- package/src/simulation/timing/sectorPerformance.js +14 -6
- package/src/simulation/timing/timingLines.js +12 -6
- package/src/simulation/track/pitLaneAccess.js +2 -2
- package/src/simulation/track/pitLaneLayout.js +4 -135
- package/src/simulation/track/pitLaneModel.js +70 -0
- package/src/simulation/track/pitLaneState.js +0 -1
- package/src/simulation/track/pitServiceLayout.js +121 -0
- package/src/simulation/track/proceduralTrack.js +77 -0
- package/src/simulation/track/spatialQueries.js +76 -31
- package/src/simulation/track/trackBuilder.js +54 -0
- package/src/simulation/track/trackConstants.js +0 -22
- package/src/simulation/track/trackMath.js +4 -29
- package/src/simulation/track/trackModel.js +12 -240
- package/src/simulation/track/trackModelCache.js +105 -0
- package/src/simulation/track/trackNearestQueries.js +1218 -0
- package/src/simulation/track/trackQueryConstants.js +3 -0
- package/src/simulation/track/trackQueryConstruction.js +368 -0
- package/src/simulation/track/trackQueryIndex.js +12 -1933
- package/src/simulation/track/trackQuerySegments.js +174 -0
- package/src/simulation/track/trackQueryStats.js +12 -0
- package/src/simulation/track/trackRayQueries.js +165 -0
- package/src/simulation/vehicle/advancedContactResponse.js +89 -0
- package/src/simulation/vehicle/advancedTireForces.js +92 -0
- package/src/simulation/vehicle/advancedVehicleModel.js +65 -0
- package/src/simulation/vehicle/advancedVehiclePhysics.js +127 -0
- package/src/simulation/vehicle/contactResolution.js +23 -9
- package/src/simulation/vehicle/mainTrackWheelSurface.js +2 -13
- package/src/simulation/vehicle/pitWheelSurface.js +0 -12
- package/src/simulation/vehicle/runoffResponse.js +2 -4
- package/src/simulation/vehicle/sampledWheelSurface.js +266 -0
- package/src/simulation/vehicle/vehicleGeometry.js +85 -103
- package/src/simulation/vehicle/vehicleKinematics.js +10 -0
- package/src/simulation/vehicle/vehiclePhysics.js +42 -196
- package/src/simulation/vehicle/vehicleSnapshots.js +18 -6
- package/src/simulation/vehicle/vehicleState.js +5 -3
- package/src/simulation/vehicle/wheelSurface.js +13 -432
- package/src/simulation/vehicle/wheelSurfaceCache.js +55 -0
- package/src/simulation/vehicle/wheelSurfaceSummary.js +101 -0
- package/src/styles.css +173 -19
- package/src/ui/bannerTemplates.js +4 -2
- package/src/ui/cameraControlsTemplate.js +4 -2
- package/src/ui/gridWalkTemplate.js +29 -0
- package/src/ui/htmlEscaping.js +8 -0
- package/src/ui/raceCanvasTemplate.js +3 -2
- package/src/ui/shellTemplate.js +3 -0
- package/src/ui/telemetryDrawerTemplate.js +4 -2
- package/src/ui/telemetryTemplates.js +3 -22
- package/src/ui/templateUtils.js +2 -8
- package/src/ui/viewControlsTemplate.js +21 -0
- package/examples/train-basic-policy.mjs +0 -276
- package/examples/trainingData.mjs +0 -133
|
@@ -110,24 +110,6 @@ function writeOrientedRectValues(target, {
|
|
|
110
110
|
return rect;
|
|
111
111
|
}
|
|
112
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, {
|
|
117
|
-
id,
|
|
118
|
-
type,
|
|
119
|
-
centerX: center.x,
|
|
120
|
-
centerY: center.y,
|
|
121
|
-
heading,
|
|
122
|
-
length,
|
|
123
|
-
width,
|
|
124
|
-
forwardX: cos,
|
|
125
|
-
forwardY: sin,
|
|
126
|
-
rightX: -sin,
|
|
127
|
-
rightY: cos,
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
|
|
131
113
|
function writeVehicleGeometry(target, pose) {
|
|
132
114
|
const geometry = target ?? {};
|
|
133
115
|
const forwardX = Math.cos(pose.heading);
|
|
@@ -238,10 +220,6 @@ function writeCurrentVehicleGeometry(target, pose) {
|
|
|
238
220
|
return geometry;
|
|
239
221
|
}
|
|
240
222
|
|
|
241
|
-
export function createOrientedRect({ id, type, center, heading, length, width }) {
|
|
242
|
-
return writeOrientedRect(null, { id, type, center, heading, length, width });
|
|
243
|
-
}
|
|
244
|
-
|
|
245
223
|
export function createVehicleGeometry(car, options = {}) {
|
|
246
224
|
const pose = vehiclePose(car, options);
|
|
247
225
|
return writeVehicleGeometry(null, pose);
|
|
@@ -274,33 +252,22 @@ function sweptPoseSignature(pose) {
|
|
|
274
252
|
].join(':');
|
|
275
253
|
}
|
|
276
254
|
|
|
277
|
-
function geometryPose(car) {
|
|
278
|
-
const { x, y, heading } = currentPoseState(car);
|
|
279
|
-
return {
|
|
280
|
-
x,
|
|
281
|
-
y,
|
|
282
|
-
heading,
|
|
283
|
-
previousX: finiteOr(car.previousX, x),
|
|
284
|
-
previousY: finiteOr(car.previousY, y),
|
|
285
|
-
previousHeading: normalizeAngle(finiteOr(car.previousHeading, heading)),
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
|
|
289
255
|
function currentGeometryStateMatches(car, state) {
|
|
290
256
|
const pose = state?.pose;
|
|
291
257
|
if (!pose) return false;
|
|
292
|
-
const currentPose = currentPoseState(car);
|
|
293
258
|
return (
|
|
294
|
-
pose.x ===
|
|
295
|
-
pose.y ===
|
|
296
|
-
pose.heading ===
|
|
259
|
+
pose.x === finiteOr(car.x, 0) &&
|
|
260
|
+
pose.y === finiteOr(car.y, 0) &&
|
|
261
|
+
pose.heading === normalizeAngle(finiteOr(car.heading, 0))
|
|
297
262
|
);
|
|
298
263
|
}
|
|
299
264
|
|
|
300
265
|
function geometryStateMatches(car, state) {
|
|
301
266
|
const pose = state?.pose;
|
|
302
267
|
if (!pose) return false;
|
|
303
|
-
const
|
|
268
|
+
const x = finiteOr(car.x, 0);
|
|
269
|
+
const y = finiteOr(car.y, 0);
|
|
270
|
+
const heading = normalizeAngle(finiteOr(car.heading, 0));
|
|
304
271
|
return (
|
|
305
272
|
pose.x === x &&
|
|
306
273
|
pose.y === y &&
|
|
@@ -312,6 +279,10 @@ function geometryStateMatches(car, state) {
|
|
|
312
279
|
}
|
|
313
280
|
|
|
314
281
|
export function createVehicleShapeAabb(shape) {
|
|
282
|
+
return writeVehicleShapeAabb({}, shape);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function writeVehicleShapeAabb(target, shape) {
|
|
315
286
|
let minX = Infinity;
|
|
316
287
|
let maxX = -Infinity;
|
|
317
288
|
let minY = Infinity;
|
|
@@ -323,55 +294,80 @@ export function createVehicleShapeAabb(shape) {
|
|
|
323
294
|
if (corner.y < minY) minY = corner.y;
|
|
324
295
|
if (corner.y > maxY) maxY = corner.y;
|
|
325
296
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
};
|
|
297
|
+
target.minX = minX;
|
|
298
|
+
target.maxX = maxX;
|
|
299
|
+
target.minY = minY;
|
|
300
|
+
target.maxY = maxY;
|
|
301
|
+
return target;
|
|
332
302
|
}
|
|
333
303
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
return { minX, maxX, minY, maxY };
|
|
304
|
+
function writeGeometryPose(target, car) {
|
|
305
|
+
const pose = target ?? {};
|
|
306
|
+
const x = finiteOr(car.x, 0);
|
|
307
|
+
const y = finiteOr(car.y, 0);
|
|
308
|
+
const heading = normalizeAngle(finiteOr(car.heading, 0));
|
|
309
|
+
pose.x = x;
|
|
310
|
+
pose.y = y;
|
|
311
|
+
pose.heading = heading;
|
|
312
|
+
pose.previousX = finiteOr(car.previousX, x);
|
|
313
|
+
pose.previousY = finiteOr(car.previousY, y);
|
|
314
|
+
pose.previousHeading = normalizeAngle(finiteOr(car.previousHeading, heading));
|
|
315
|
+
return pose;
|
|
347
316
|
}
|
|
348
317
|
|
|
349
|
-
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
318
|
+
function createGeometryStateShell() {
|
|
319
|
+
const state = {
|
|
320
|
+
pose: null,
|
|
321
|
+
current: null,
|
|
322
|
+
previous: null,
|
|
323
|
+
body: null,
|
|
324
|
+
wheels: null,
|
|
325
|
+
contactPatches: null,
|
|
326
|
+
bodyAabb: {},
|
|
327
|
+
previousBodyAabb: {},
|
|
328
|
+
sweptBodyAabb: {},
|
|
329
|
+
};
|
|
330
|
+
// Signatures are derived lazily from the pose: nothing on the hot path reads
|
|
331
|
+
// them, so the per-step string formatting cost must not be paid eagerly.
|
|
332
|
+
Object.defineProperties(state, {
|
|
333
|
+
currentSignature: {
|
|
334
|
+
get() { return currentPoseSignature(this.pose); },
|
|
335
|
+
enumerable: true,
|
|
336
|
+
},
|
|
337
|
+
sweptSignature: {
|
|
338
|
+
get() { return sweptPoseSignature(this.pose); },
|
|
339
|
+
enumerable: true,
|
|
340
|
+
},
|
|
341
|
+
signature: {
|
|
342
|
+
get() { return sweptPoseSignature(this.pose); },
|
|
343
|
+
enumerable: true,
|
|
344
|
+
},
|
|
356
345
|
});
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
const sweptBodyAabb = mergeAabbs([previousBodyAabb, bodyAabb]);
|
|
346
|
+
return state;
|
|
347
|
+
}
|
|
360
348
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
349
|
+
const PREVIOUS_GEOMETRY_POSE_SCRATCH = { x: 0, y: 0, heading: 0 };
|
|
350
|
+
|
|
351
|
+
export function createVehicleGeometryState(car, target = null) {
|
|
352
|
+
const state = target ?? createGeometryStateShell();
|
|
353
|
+
const pose = state.pose = writeGeometryPose(state.pose, car);
|
|
354
|
+
const current = state.current = writeVehicleGeometry(state.current, pose);
|
|
355
|
+
PREVIOUS_GEOMETRY_POSE_SCRATCH.x = pose.previousX;
|
|
356
|
+
PREVIOUS_GEOMETRY_POSE_SCRATCH.y = pose.previousY;
|
|
357
|
+
PREVIOUS_GEOMETRY_POSE_SCRATCH.heading = pose.previousHeading;
|
|
358
|
+
const previous = state.previous = writeVehicleGeometry(state.previous, PREVIOUS_GEOMETRY_POSE_SCRATCH);
|
|
359
|
+
const bodyAabb = writeVehicleShapeAabb(state.bodyAabb, current.body);
|
|
360
|
+
const previousBodyAabb = writeVehicleShapeAabb(state.previousBodyAabb, previous.body);
|
|
361
|
+
const sweptBodyAabb = state.sweptBodyAabb;
|
|
362
|
+
sweptBodyAabb.minX = Math.min(bodyAabb.minX, previousBodyAabb.minX);
|
|
363
|
+
sweptBodyAabb.maxX = Math.max(bodyAabb.maxX, previousBodyAabb.maxX);
|
|
364
|
+
sweptBodyAabb.minY = Math.min(bodyAabb.minY, previousBodyAabb.minY);
|
|
365
|
+
sweptBodyAabb.maxY = Math.max(bodyAabb.maxY, previousBodyAabb.maxY);
|
|
366
|
+
|
|
367
|
+
state.body = current.body;
|
|
368
|
+
state.wheels = current.wheels;
|
|
369
|
+
state.contactPatches = current.contactPatches;
|
|
370
|
+
return state;
|
|
375
371
|
}
|
|
376
372
|
|
|
377
373
|
export function createCurrentVehicleGeometryState(car, target = null) {
|
|
@@ -388,8 +384,13 @@ export function createCurrentVehicleGeometryState(car, target = null) {
|
|
|
388
384
|
|
|
389
385
|
export function getVehicleGeometryState(car) {
|
|
390
386
|
if (geometryStateMatches(car, car.geometryState)) return car.geometryState;
|
|
391
|
-
|
|
392
|
-
|
|
387
|
+
// Double-buffer pooled states: consumers may still hold the state returned
|
|
388
|
+
// for the previous pose within a step, so alternate between two reusable
|
|
389
|
+
// shells instead of allocating a fresh geometry tree on every pose change.
|
|
390
|
+
const next = createVehicleGeometryState(car, car._geometryStateSpare ?? null);
|
|
391
|
+
car._geometryStateSpare = car.geometryState ?? null;
|
|
392
|
+
car.geometryState = next;
|
|
393
|
+
return next;
|
|
393
394
|
}
|
|
394
395
|
|
|
395
396
|
export function getCurrentVehicleGeometryState(car) {
|
|
@@ -417,25 +418,6 @@ export function ensureOrientedRectCorners(rect) {
|
|
|
417
418
|
}).corners;
|
|
418
419
|
}
|
|
419
420
|
|
|
420
|
-
export function createVehicleAabb(car, options = {}) {
|
|
421
|
-
const geometry = createVehicleGeometry(car, options);
|
|
422
|
-
return mergeAabbs(geometry.shapes.map(createVehicleShapeAabb));
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
export function interpolateVehiclePose(car, amount) {
|
|
426
|
-
const previousX = finiteOr(car.previousX, car.x);
|
|
427
|
-
const previousY = finiteOr(car.previousY, car.y);
|
|
428
|
-
const previousHeading = finiteOr(car.previousHeading, car.heading);
|
|
429
|
-
const headingDelta = normalizeAngle(finiteOr(car.heading, 0) - previousHeading);
|
|
430
|
-
|
|
431
|
-
return {
|
|
432
|
-
...car,
|
|
433
|
-
x: previousX + (finiteOr(car.x, previousX) - previousX) * amount,
|
|
434
|
-
y: previousY + (finiteOr(car.y, previousY) - previousY) * amount,
|
|
435
|
-
heading: normalizeAngle(previousHeading + headingDelta * amount),
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
|
|
439
421
|
export function getCarCorners(car) {
|
|
440
422
|
return createVehicleGeometry(car).body.corners;
|
|
441
423
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { normalizeAngle } from '../simMath.js';
|
|
2
|
+
|
|
1
3
|
export function freezeVehicleMotion(car, {
|
|
2
4
|
brake = 1,
|
|
3
5
|
clearManualControls = false,
|
|
@@ -35,3 +37,11 @@ export function syncVehicleVelocityToHeading(car) {
|
|
|
35
37
|
car.velocityX = Math.cos(heading) * speed;
|
|
36
38
|
car.velocityY = Math.sin(heading) * speed;
|
|
37
39
|
}
|
|
40
|
+
|
|
41
|
+
export function shiftPreviousRenderPose(car, dx, dy, dheading = 0) {
|
|
42
|
+
car.previousX = Number.isFinite(car.previousX) ? car.previousX + dx : car.x + dx;
|
|
43
|
+
car.previousY = Number.isFinite(car.previousY) ? car.previousY + dy : car.y + dy;
|
|
44
|
+
car.previousHeading = normalizeAngle(
|
|
45
|
+
(Number.isFinite(car.previousHeading) ? car.previousHeading : car.heading) + dheading,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { integrateAdvancedVehiclePhysics } from './advancedVehiclePhysics.js';
|
|
1
2
|
import { clamp, normalizeAngle } from '../simMath.js';
|
|
2
3
|
import {
|
|
3
4
|
REAL_F1_WHEELBASE_METERS,
|
|
4
5
|
SIM_UNITS_PER_METER,
|
|
5
6
|
TOP_SPEED_SIM_UNITS_PER_SECOND,
|
|
6
|
-
metersPerSecondToSimSpeed,
|
|
7
7
|
simUnitsToMeters,
|
|
8
8
|
metersToSimUnits,
|
|
9
9
|
} from '../units.js';
|
|
@@ -37,20 +37,12 @@ const SURFACE_MODEL = {
|
|
|
37
37
|
const WHEEL_DRAG_YAW_GAIN = 0.08;
|
|
38
38
|
const MAX_WHEEL_DRAG_YAW_RATE = 0.22;
|
|
39
39
|
|
|
40
|
-
const SIMULATOR_SURFACE_MODEL = {
|
|
41
|
-
track: { grip: 1, drag: 0, rollingResistance: 0.008, scrub: 0 },
|
|
42
|
-
'pit-entry': { grip: 0.92, drag: 0.06, rollingResistance: 0.018, scrub: 1.2 },
|
|
43
|
-
'pit-lane': { grip: 0.9, drag: 0.08, rollingResistance: 0.02, scrub: 1.4 },
|
|
44
|
-
'pit-exit': { grip: 0.92, drag: 0.06, rollingResistance: 0.018, scrub: 1.2 },
|
|
45
|
-
'pit-box': { grip: 0.82, drag: 0.12, rollingResistance: 0.035, scrub: 2.5 },
|
|
46
|
-
kerb: { grip: 0.78, drag: 0.42, rollingResistance: 0.09, scrub: 8.5 },
|
|
47
|
-
gravel: { grip: 0.36, drag: 5.4, rollingResistance: 0.24, scrub: 18 },
|
|
48
|
-
grass: { grip: 0.3, drag: 3.4, rollingResistance: 0.2, scrub: 15 },
|
|
49
|
-
barrier: { grip: 0.08, drag: 15, rollingResistance: 2.4, scrub: 30 },
|
|
50
|
-
};
|
|
51
|
-
|
|
52
40
|
export function normalizePhysicsMode(value) {
|
|
53
|
-
return value
|
|
41
|
+
return PHYSICS_MODES.includes(value) ? value : DEFAULT_PHYSICS_MODE;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function isSimulatorPhysicsMode(value) {
|
|
45
|
+
return value === 'advanced';
|
|
54
46
|
}
|
|
55
47
|
|
|
56
48
|
function accelerationLimit(speed, surfaceGrip) {
|
|
@@ -63,11 +55,39 @@ function brakingLimit(speed, surfaceGrip) {
|
|
|
63
55
|
return SIM_UNITS_PER_METER * surfaceGrip * (20 + speedRatio * 14);
|
|
64
56
|
}
|
|
65
57
|
|
|
58
|
+
// Policy accessors use the same arcade model without changing its integrator.
|
|
59
|
+
// Surface grip is the base coefficient; callers apply tire condition once.
|
|
60
|
+
export function arcadeSurfaceGrip(car) {
|
|
61
|
+
return (SURFACE_MODEL[car.trackState?.surface] ?? SURFACE_MODEL.track).grip;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function arcadeResistanceForce(car, speedMetersPerSecond) {
|
|
65
|
+
const surface = SURFACE_MODEL[car.trackState?.surface] ?? SURFACE_MODEL.track;
|
|
66
|
+
const dragMultiplier = car.drsActive ? 0.42 : 1;
|
|
67
|
+
return (car.dragCoefficient * dragMultiplier * 0.12 + surface.drag) * speedMetersPerSecond ** 2 +
|
|
68
|
+
surface.rollingResistance * car.mass * G;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// The existing limits take speed and return acceleration in simulator units.
|
|
72
|
+
export { accelerationLimit as arcadeAccelerationLimit, brakingLimit as arcadeBrakingLimit };
|
|
73
|
+
|
|
66
74
|
export function tirePerformanceFactor(tireEnergy = 100) {
|
|
67
75
|
const normalized = clamp((Number(tireEnergy) || 1) / 100, 0.01, 1);
|
|
68
76
|
return clamp(0.45 + 0.55 * normalized ** 0.72, 0.45, 1);
|
|
69
77
|
}
|
|
70
78
|
|
|
79
|
+
export function arcadeTrackCornerSpeedLimit(car, curvature, gripShare) {
|
|
80
|
+
const tire = tirePerformanceFactor(car.tireEnergy ?? 100);
|
|
81
|
+
// On tarmac the arcade yaw limit is (static + aero * speed²) / speed.
|
|
82
|
+
// Invert that same law for a future corner; the driver owns its grip reserve.
|
|
83
|
+
const staticCapacity = car.tireGrip * G * tire * tire;
|
|
84
|
+
const aeroCapacity = car.downforceCoefficient / car.mass * tire;
|
|
85
|
+
const denominator = Math.abs(curvature) - gripShare * aeroCapacity;
|
|
86
|
+
return denominator > 0
|
|
87
|
+
? Math.min(simUnitsToMeters(VEHICLE_LIMITS.maxSpeed), Math.sqrt(gripShare * staticCapacity / denominator))
|
|
88
|
+
: simUnitsToMeters(VEHICLE_LIMITS.maxSpeed);
|
|
89
|
+
}
|
|
90
|
+
|
|
71
91
|
function surfaceResistance(surfaceName, model = SURFACE_MODEL) {
|
|
72
92
|
const surface = model[surfaceName] ?? model.track;
|
|
73
93
|
return surface.drag * 0.14 + surface.rollingResistance * 2.4 + (1 - surface.grip) * 0.65;
|
|
@@ -120,8 +140,14 @@ function setPhysicsTelemetry(car, {
|
|
|
120
140
|
}
|
|
121
141
|
|
|
122
142
|
export function integrateVehiclePhysics(car, controls, dt, options = {}) {
|
|
123
|
-
|
|
124
|
-
|
|
143
|
+
const mode = normalizePhysicsMode(options.physicsMode);
|
|
144
|
+
if (mode === 'advanced') {
|
|
145
|
+
return integrateAdvancedVehiclePhysics(car, controls, dt, {
|
|
146
|
+
tireFactor: tirePerformanceFactor(car.tireEnergy ?? 100),
|
|
147
|
+
tireDegradationEnabled: options.tireDegradationEnabled,
|
|
148
|
+
maxSteer: VEHICLE_LIMITS.maxSteer,
|
|
149
|
+
steerRate: VEHICLE_LIMITS.steerRate,
|
|
150
|
+
});
|
|
125
151
|
}
|
|
126
152
|
return integrateArcadeVehiclePhysics(car, controls, dt, options);
|
|
127
153
|
}
|
|
@@ -196,184 +222,4 @@ function integrateArcadeVehiclePhysics(car, controls, dt, options = {}) {
|
|
|
196
222
|
return car;
|
|
197
223
|
}
|
|
198
224
|
|
|
199
|
-
function simulatorSurface(surfaceName) {
|
|
200
|
-
return SIMULATOR_SURFACE_MODEL[surfaceName] ?? SIMULATOR_SURFACE_MODEL.track;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function aggregateSimulatorSurface(car) {
|
|
204
|
-
const wheels = Array.isArray(car.wheelStates) ? car.wheelStates : [];
|
|
205
|
-
if (!wheels.length) return simulatorSurface(car.trackState?.surface ?? 'track');
|
|
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
|
-
}
|
|
217
|
-
return {
|
|
218
|
-
grip: grip / wheels.length,
|
|
219
|
-
drag: drag / wheels.length,
|
|
220
|
-
rollingResistance: rollingResistance / wheels.length,
|
|
221
|
-
scrub: scrub / wheels.length,
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function simulatorStabilityState({ gripUsage, slipAngle, surfaceName, brake, throttle }) {
|
|
226
|
-
if (gripUsage > 1.32 || Math.abs(slipAngle) > 0.42) return 'spin-risk';
|
|
227
|
-
if (gripUsage > 1.02) return brake > 0.2 && throttle < 0.2 ? 'oversteer' : 'understeer';
|
|
228
|
-
if (surfaceName === 'kerb' && gripUsage > 0.72) return 'oversteer';
|
|
229
|
-
if ((surfaceName === 'gravel' || surfaceName === 'grass') && gripUsage > 0.5) return 'understeer';
|
|
230
|
-
return 'stable';
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function ensureSimulatorVelocity(car) {
|
|
234
|
-
if (Number.isFinite(car.velocityX) && Number.isFinite(car.velocityY)) return;
|
|
235
|
-
const speed = clamp(Number(car.speed) || 0, 0, VEHICLE_LIMITS.maxSpeed);
|
|
236
|
-
car.velocityX = Math.cos(car.heading ?? 0) * speed;
|
|
237
|
-
car.velocityY = Math.sin(car.heading ?? 0) * speed;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function limitSimulatorSpeed(car) {
|
|
241
|
-
const speed = Math.hypot(car.velocityX, car.velocityY);
|
|
242
|
-
if (speed <= VEHICLE_LIMITS.maxSpeed || speed <= 0) return speed;
|
|
243
|
-
const scale = VEHICLE_LIMITS.maxSpeed / speed;
|
|
244
|
-
car.velocityX *= scale;
|
|
245
|
-
car.velocityY *= scale;
|
|
246
|
-
return VEHICLE_LIMITS.maxSpeed;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function integrateSimulatorVehiclePhysics(car, controls, dt, options = {}) {
|
|
250
|
-
ensureSimulatorVelocity(car);
|
|
251
|
-
const steeringTarget = clamp(controls.steering ?? 0, -VEHICLE_LIMITS.maxSteer, VEHICLE_LIMITS.maxSteer);
|
|
252
|
-
const steerDelta = clamp(
|
|
253
|
-
steeringTarget - car.steeringAngle,
|
|
254
|
-
-VEHICLE_LIMITS.steerRate * dt,
|
|
255
|
-
VEHICLE_LIMITS.steerRate * dt,
|
|
256
|
-
);
|
|
257
|
-
car.steeringAngle += steerDelta;
|
|
258
|
-
|
|
259
|
-
const throttle = clamp(controls.throttle ?? 0, 0, 1);
|
|
260
|
-
const brake = clamp(controls.brake ?? 0, 0, 1);
|
|
261
|
-
const surfaceName = car.trackState?.surface ?? 'track';
|
|
262
|
-
const surface = aggregateSimulatorSurface(car);
|
|
263
|
-
const tireFactor = tirePerformanceFactor(car.tireEnergy ?? 100);
|
|
264
|
-
const surfaceGrip = surface.grip * tireFactor;
|
|
265
|
-
const speedBefore = Math.hypot(car.velocityX, car.velocityY);
|
|
266
|
-
const speedBeforeMps = simUnitsToMeters(speedBefore);
|
|
267
|
-
const speedRatio = clamp(speedBefore / VEHICLE_LIMITS.maxSpeed, 0, 1);
|
|
268
|
-
const downforceGrip = car.downforceCoefficient * speedBeforeMps * speedBeforeMps / car.mass;
|
|
269
|
-
const totalGripAcceleration = Math.max(0.1, (car.tireGrip * tireFactor * G + downforceGrip) * surfaceGrip);
|
|
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;
|
|
277
|
-
const forwardSpeedMps = simUnitsToMeters(forwardVelocity);
|
|
278
|
-
const lateralSpeedMps = simUnitsToMeters(lateralVelocity);
|
|
279
|
-
const currentSlipAngle = Math.atan2(lateralSpeedMps, Math.max(Math.abs(forwardSpeedMps), 0.1));
|
|
280
|
-
const engineForce = throttle *
|
|
281
|
-
car.powerNewtons *
|
|
282
|
-
(car.drsActive ? 1.07 : 1) *
|
|
283
|
-
Math.max(0.08, 1 - speedRatio ** 1.22);
|
|
284
|
-
const brakeForce = brake * car.brakeNewtons;
|
|
285
|
-
const driveAcceleration = Math.min(
|
|
286
|
-
engineForce / car.mass,
|
|
287
|
-
accelerationLimit(car.speed, surfaceGrip) / SIM_UNITS_PER_METER,
|
|
288
|
-
);
|
|
289
|
-
const brakeDeceleration = Math.min(
|
|
290
|
-
brakeForce / car.mass,
|
|
291
|
-
brakingLimit(car.speed, surfaceGrip) / SIM_UNITS_PER_METER,
|
|
292
|
-
);
|
|
293
|
-
const dragMultiplier = car.drsActive ? 0.46 : 1;
|
|
294
|
-
const dragAcceleration = (
|
|
295
|
-
(car.dragCoefficient * dragMultiplier * 0.28 + surface.drag * 0.018) *
|
|
296
|
-
speedBeforeMps * speedBeforeMps
|
|
297
|
-
) / car.mass;
|
|
298
|
-
const rollingAcceleration = surface.rollingResistance * G;
|
|
299
|
-
const normalizedSteer = Math.abs(car.steeringAngle) / VEHICLE_LIMITS.maxSteer;
|
|
300
|
-
const speedSensitiveSteer = 1 / (1 + (Math.abs(forwardSpeedMps) / 58) ** 1.85 * 2.35);
|
|
301
|
-
const effectiveSteeringAngle = car.steeringAngle * clamp(speedSensitiveSteer, 0.16, 1);
|
|
302
|
-
const rawYawRate = Math.abs(forwardSpeedMps) <= 0.05
|
|
303
|
-
? 0
|
|
304
|
-
: (forwardSpeedMps / REAL_F1_WHEELBASE_METERS) * Math.tan(effectiveSteeringAngle);
|
|
305
|
-
const sideSlipDamping = 4.2 + surfaceGrip * 2.1 + speedRatio * 1.6;
|
|
306
|
-
const desiredLateralAcceleration = forwardSpeedMps * rawYawRate - lateralSpeedMps * sideSlipDamping;
|
|
307
|
-
const longitudinalDemand = Math.max(driveAcceleration, brakeDeceleration);
|
|
308
|
-
const longitudinalUsage = clamp(longitudinalDemand / Math.max(totalGripAcceleration * 0.52, 1), 0, 0.94);
|
|
309
|
-
const lateralCapacity = totalGripAcceleration * Math.sqrt(Math.max(0.08, 1 - longitudinalUsage ** 2));
|
|
310
|
-
const desiredLateralAbs = Math.abs(desiredLateralAcceleration);
|
|
311
|
-
const lateralUsage = desiredLateralAbs / Math.max(lateralCapacity, 0.1);
|
|
312
|
-
const gripUsage = Math.sqrt(lateralUsage ** 2 + longitudinalUsage ** 2);
|
|
313
|
-
const tractionLimited = gripUsage > 1 || longitudinalUsage > 0.9;
|
|
314
|
-
const actualLateralAcceleration = clamp(desiredLateralAcceleration, -lateralCapacity, lateralCapacity);
|
|
315
|
-
const steeringYawRate = Math.abs(forwardSpeedMps) <= 0.05 ? 0 : actualLateralAcceleration / Math.max(Math.abs(forwardSpeedMps), 0.1);
|
|
316
|
-
const steeringScrubAcceleration = normalizedSteer ** 1.55 *
|
|
317
|
-
speedRatio ** 1.05 *
|
|
318
|
-
(42 + surface.scrub + Math.max(0, gripUsage - 0.55) * 18);
|
|
319
|
-
const tractionPowerScale = tractionLimited
|
|
320
|
-
? clamp(1 - (gripUsage - 1) * 0.55 - longitudinalUsage * 0.18, 0.12, 1)
|
|
321
|
-
: 1;
|
|
322
|
-
const brakingDirection = forwardSpeedMps >= -0.2 ? 1 : -1;
|
|
323
|
-
const scrubDirection = forwardSpeedMps >= 0 ? 1 : -1;
|
|
324
|
-
const longitudinalAcceleration =
|
|
325
|
-
driveAcceleration * tractionPowerScale -
|
|
326
|
-
brakeDeceleration * brakingDirection -
|
|
327
|
-
dragAcceleration * Math.sign(forwardSpeedMps || 1) -
|
|
328
|
-
rollingAcceleration * Math.sign(forwardSpeedMps || 1) -
|
|
329
|
-
steeringScrubAcceleration * scrubDirection;
|
|
330
|
-
|
|
331
|
-
const requestedWheelDragYawRate = wheelDragYawRate(car, SIMULATOR_SURFACE_MODEL) * (surfaceName === 'kerb' ? 1.6 : 1);
|
|
332
|
-
const maxYawRate = speedBeforeMps <= 0.05 ? 0 : totalGripAcceleration / Math.max(speedBeforeMps, 0.1);
|
|
333
|
-
const slipSteeringAuthority = clamp(1 - Math.abs(currentSlipAngle) * 1.8, 0.35, 1);
|
|
334
|
-
const yawTarget = clamp(steeringYawRate * slipSteeringAuthority + requestedWheelDragYawRate, -maxYawRate, maxYawRate);
|
|
335
|
-
const yawResponse = 1.6 + surfaceGrip * 0.9 + speedRatio * 0.5;
|
|
336
|
-
const yawAccelerationCap = speedRatio < 0.18 ? 6.2 : 3.2;
|
|
337
|
-
const maxYawDelta = Math.min(Math.max(0.08, maxYawRate * yawResponse), yawAccelerationCap) * dt;
|
|
338
|
-
car.yawRate = clamp(
|
|
339
|
-
(car.yawRate ?? 0) + clamp(yawTarget - (car.yawRate ?? 0), -maxYawDelta, maxYawDelta),
|
|
340
|
-
-maxYawRate,
|
|
341
|
-
maxYawRate,
|
|
342
|
-
);
|
|
343
|
-
car.wheelDragYawRate = requestedWheelDragYawRate;
|
|
344
|
-
car.heading = normalizeAngle(car.heading + car.yawRate * dt);
|
|
345
|
-
|
|
346
|
-
const accelerationX = forwardX * longitudinalAcceleration + rightX * actualLateralAcceleration;
|
|
347
|
-
const accelerationY = forwardY * longitudinalAcceleration + rightY * actualLateralAcceleration;
|
|
348
|
-
car.velocityX += metersToSimUnits(accelerationX) * dt;
|
|
349
|
-
car.velocityY += metersToSimUnits(accelerationY) * dt;
|
|
350
|
-
car.speed = limitSimulatorSpeed(car);
|
|
351
|
-
car.x += car.velocityX * dt;
|
|
352
|
-
car.y += car.velocityY * dt;
|
|
353
|
-
car.throttle = throttle;
|
|
354
|
-
car.brake = brake;
|
|
355
|
-
car.steerSaturation = rawYawRate === 0 ? 0 : Math.abs(steeringYawRate / rawYawRate);
|
|
356
|
-
car.turnRadius = Math.abs(car.yawRate) < 0.001 ? Infinity : car.speed / Math.abs(car.yawRate);
|
|
357
|
-
const velocityHeading = car.speed <= 0.01 ? car.heading : Math.atan2(car.velocityY, car.velocityX);
|
|
358
|
-
const slipAngle = normalizeAngle(velocityHeading - car.heading);
|
|
359
|
-
setPhysicsTelemetry(car, {
|
|
360
|
-
lateralAcceleration: actualLateralAcceleration,
|
|
361
|
-
longitudinalAcceleration,
|
|
362
|
-
gripUsage,
|
|
363
|
-
slipAngleRadians: slipAngle,
|
|
364
|
-
tractionLimited,
|
|
365
|
-
stabilityState: simulatorStabilityState({ gripUsage, slipAngle, surfaceName, brake, throttle }),
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
if (options.tireDegradationEnabled !== false) {
|
|
369
|
-
const tyreLoad = Math.abs(car.lateralAcceleration) / G;
|
|
370
|
-
const tireCare = clamp(Number(car.tireCare) || 1, 0.45, 1.8);
|
|
371
|
-
const heatPenalty = Math.max(0, gripUsage - 0.72) * 0.24 + normalizedSteer * speedRatio * 0.08;
|
|
372
|
-
const wearRate = (0.045 + tyreLoad * 0.16 + brake * 0.09 + throttle * 0.035 + heatPenalty) / tireCare;
|
|
373
|
-
car.tireEnergy = clamp((car.tireEnergy ?? 100) - wearRate * dt, 1, 100);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
return car;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
225
|
export { getCarCorners };
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { finiteOrNull } from '../simMath.js';
|
|
2
|
+
export { finiteOrNull } from '../simMath.js';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
serializeParticipantInteraction,
|
|
6
|
+
serializeParticipantInteractionCached,
|
|
7
|
+
} from '../participants/participantInteractions.js';
|
|
2
8
|
|
|
3
|
-
export function finiteOrNull(value) {
|
|
4
|
-
return Number.isFinite(value) ? value : null;
|
|
5
|
-
}
|
|
6
9
|
|
|
7
10
|
function serializeAppliedControls(car, dependencies) {
|
|
8
11
|
const { VEHICLE_LIMITS } = dependencies;
|
|
@@ -21,6 +24,15 @@ function serializeVelocityComponent(car, component, dependencies) {
|
|
|
21
24
|
return finiteOrNull(car[component]);
|
|
22
25
|
}
|
|
23
26
|
|
|
27
|
+
function serializeTeam(team) {
|
|
28
|
+
if (!team) return null;
|
|
29
|
+
return {
|
|
30
|
+
...team,
|
|
31
|
+
...(team.pitCrew && typeof team.pitCrew === 'object' ? { pitCrew: { ...team.pitCrew } } : {}),
|
|
32
|
+
...(team.pitCrewStats && typeof team.pitCrewStats === 'object' ? { pitCrewStats: { ...team.pitCrewStats } } : {}),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
export function serializeWheels(wheels = []) {
|
|
25
37
|
return wheels.map((wheel) => ({
|
|
26
38
|
id: wheel.id,
|
|
@@ -57,7 +69,7 @@ export function serializeCar(car, rank, penaltySeconds = 0, dependencies) {
|
|
|
57
69
|
raceName: car.raceName,
|
|
58
70
|
name: car.name,
|
|
59
71
|
color: car.color,
|
|
60
|
-
team: car.team
|
|
72
|
+
team: serializeTeam(car.team),
|
|
61
73
|
tire: car.tire,
|
|
62
74
|
personality: { ...car.personality },
|
|
63
75
|
aggression: car.aggression,
|
|
@@ -185,7 +197,7 @@ export function serializeRenderCarInto(target, car, dependencies) {
|
|
|
185
197
|
target.dnfOrder = car.dnfOrder ?? null;
|
|
186
198
|
target.outOfRace = Boolean(car.outOfRace || car.destroyed);
|
|
187
199
|
target.pitStop = serializeRenderPitStop(car.pitStop);
|
|
188
|
-
target.interaction =
|
|
200
|
+
target.interaction = serializeParticipantInteractionCached(car);
|
|
189
201
|
return target;
|
|
190
202
|
}
|
|
191
203
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { buildDriverPersonality } from '../
|
|
1
|
+
import { buildDriverPersonality } from '../driver/driverPersonality.js';
|
|
2
2
|
import { clamp, normalizeAngle, seededRange } from '../simMath.js';
|
|
3
3
|
import { offsetTrackPoint, pointAt } from '../track/trackModel.js';
|
|
4
4
|
import { kphToSimSpeed, metersToSimUnits } from '../units.js';
|
|
5
|
-
import { VEHICLE_LIMITS } from './vehiclePhysics.js';
|
|
5
|
+
import { VEHICLE_LIMITS, isSimulatorPhysicsMode } from './vehiclePhysics.js';
|
|
6
6
|
import { applyWheelSurfaceState } from './wheelSurface.js';
|
|
7
7
|
import { nearestTrackStateForCar } from '../track/trackStatePolicy.js';
|
|
8
8
|
import { clearCarDnf } from '../race/retirements.js';
|
|
@@ -73,8 +73,10 @@ export function createCar(driver, index, random, track, { standingStart = false,
|
|
|
73
73
|
downforceCoefficient: vehicle.downforceCoefficient ?? 6.1 + seededRange(random, -0.18, 0.18),
|
|
74
74
|
tireGrip: vehicle.tireGrip ?? 2.22 + racecraft * 0.28 + seededRange(random, -0.03, 0.03),
|
|
75
75
|
tireCare: vehicle.tireCare ?? 1,
|
|
76
|
+
driverModel: driver.driverModel ?? vehicle.driverModel ?? null,
|
|
76
77
|
pace,
|
|
77
78
|
racecraft,
|
|
79
|
+
consistency: Number.isFinite(driver.consistency) ? clamp(driver.consistency, 0, 1) : 0.75,
|
|
78
80
|
personality,
|
|
79
81
|
aggression: personality.baseAggression,
|
|
80
82
|
gridLocked: standingStart,
|
|
@@ -170,7 +172,7 @@ export function applyExternalCarState(car, partial, context) {
|
|
|
170
172
|
}
|
|
171
173
|
car.speed = clamp(Number.isFinite(car.speed) ? car.speed : 0, 0, VEHICLE_LIMITS.maxSpeed);
|
|
172
174
|
car.heading = normalizeAngle(Number.isFinite(car.heading) ? car.heading : 0);
|
|
173
|
-
syncExternalVelocityState(car, nextPartial, physicsMode
|
|
175
|
+
syncExternalVelocityState(car, nextPartial, isSimulatorPhysicsMode(physicsMode));
|
|
174
176
|
const centerState = nearestTrackStateForCar(track, car, car, car.progress ?? car.raceDistance);
|
|
175
177
|
applyWheelSurfaceState(car, track, { centerState });
|
|
176
178
|
if (
|