@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
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { clamp, normalizeAngle } from '../simMath.js';
|
|
2
|
+
import { SIM_UNITS_PER_METER, TOP_SPEED_SIM_UNITS_PER_SECOND } from '../units.js';
|
|
3
|
+
import { ADVANCED_VEHICLE_MODEL as MODEL, ADVANCED_WHEELS, advancedWheelSurface, advancedDriveForce, advancedDragFactor } from './advancedVehicleModel.js';
|
|
4
|
+
import { advancedNormalLoads, advancedTireForce } from './advancedTireForces.js';
|
|
5
|
+
|
|
6
|
+
export function integrateAdvancedVehiclePhysics(car, controls, dt, { tireFactor, tireDegradationEnabled, maxSteer, steerRate }) {
|
|
7
|
+
if (!(dt > 0) || !Number.isFinite(dt)) return car;
|
|
8
|
+
if (!Number.isFinite(car.velocityX) || !Number.isFinite(car.velocityY)) {
|
|
9
|
+
car.velocityX = Math.cos(car.heading) * car.speed;
|
|
10
|
+
car.velocityY = Math.sin(car.heading) * car.speed;
|
|
11
|
+
}
|
|
12
|
+
const throttle = clamp(controls.throttle ?? 0, 0, 1);
|
|
13
|
+
const brake = clamp(controls.brake ?? 0, 0, 1);
|
|
14
|
+
const steeringTarget = clamp(controls.steering ?? 0, -maxSteer, maxSteer);
|
|
15
|
+
const steps = Math.ceil(dt / MODEL.maxIntegrationStep);
|
|
16
|
+
const h = dt / steps;
|
|
17
|
+
const initialVelocityX = car.velocityX / SIM_UNITS_PER_METER;
|
|
18
|
+
const initialVelocityY = car.velocityY / SIM_UNITS_PER_METER;
|
|
19
|
+
const initialHeading = car.heading;
|
|
20
|
+
let frontUsage = 0;
|
|
21
|
+
let rearUsage = 0;
|
|
22
|
+
let resistanceYawAcceleration = 0;
|
|
23
|
+
for (let step = 0; step < steps; step += 1) {
|
|
24
|
+
car.steeringAngle += clamp(steeringTarget - car.steeringAngle, -steerRate * h, steerRate * h);
|
|
25
|
+
const cos = Math.cos(car.heading);
|
|
26
|
+
const sin = Math.sin(car.heading);
|
|
27
|
+
const vx = car.velocityX / SIM_UNITS_PER_METER;
|
|
28
|
+
const vy = car.velocityY / SIM_UNITS_PER_METER;
|
|
29
|
+
const u = vx * cos + vy * sin;
|
|
30
|
+
const v = -vx * sin + vy * cos;
|
|
31
|
+
const speed = Math.hypot(vx, vy);
|
|
32
|
+
const yaw = car.yawRate ?? 0;
|
|
33
|
+
const loads = advancedNormalLoads(car, speed, car.longitudinalAcceleration ?? 0, car.lateralAcceleration ?? 0);
|
|
34
|
+
// The existing newton-valued power rating defines launch force. Above the
|
|
35
|
+
// crossover speed, the envelope is constant power instead of constant force.
|
|
36
|
+
const drive = throttle * advancedDriveForce(car, u);
|
|
37
|
+
let fx = 0;
|
|
38
|
+
let fy = 0;
|
|
39
|
+
let moment = 0;
|
|
40
|
+
let resistanceMoment = 0;
|
|
41
|
+
frontUsage = 0;
|
|
42
|
+
rearUsage = 0;
|
|
43
|
+
for (let index = 0; index < ADVANCED_WHEELS.length; index += 1) {
|
|
44
|
+
const wheel = ADVANCED_WHEELS[index];
|
|
45
|
+
const { x, y } = wheel;
|
|
46
|
+
const steer = wheel.front ? car.steeringAngle : 0;
|
|
47
|
+
const cs = Math.cos(steer);
|
|
48
|
+
const sn = Math.sin(steer);
|
|
49
|
+
const wheelU = (u - yaw * y) * cs + (v + yaw * x) * sn;
|
|
50
|
+
const wheelV = -(u - yaw * y) * sn + (v + yaw * x) * cs;
|
|
51
|
+
const surface = advancedWheelSurface(car, wheel.id);
|
|
52
|
+
const engine = wheel.front ? 0 : drive / 2;
|
|
53
|
+
const brakeDemand = brake * car.brakeNewtons * (wheel.front ? MODEL.frontBrakeFraction : 1 - MODEL.frontBrakeFraction) / 2;
|
|
54
|
+
// Brake/static resistance cannot manufacture a reverse launch at rest.
|
|
55
|
+
const brakingDirection = Math.sign(wheelU || engine);
|
|
56
|
+
const longitudinalLever = x * sn - y * cs;
|
|
57
|
+
const inverseContactMass = 1 / car.mass + longitudinalLever ** 2 / (car.mass * MODEL.yawInertiaPerMass);
|
|
58
|
+
// Four simultaneous contacts share the stopping impulse. Include yaw
|
|
59
|
+
// inertia and share the budget between braking and rolling resistance;
|
|
60
|
+
// independently spending it twice would create motion near rest.
|
|
61
|
+
const stoppingBudget = Math.abs(wheelU) / (4 * h * inverseContactMass);
|
|
62
|
+
const braking = brakingDirection * Math.min(brakeDemand, stoppingBudget + engine);
|
|
63
|
+
const remainingBudget = Math.max(0, stoppingBudget - Math.max(0, (braking - engine) * Math.sign(wheelU)));
|
|
64
|
+
const rolling = Math.sign(wheelU) * Math.min(surface.rolling * loads[index], remainingBudget);
|
|
65
|
+
const nominalLoad = car.mass * MODEL.gravity * (wheel.front ? MODEL.frontWeightFraction : 1 - MODEL.frontWeightFraction) / 2;
|
|
66
|
+
const tire = advancedTireForce({
|
|
67
|
+
normalLoad: loads[index], nominalLoad, grip: car.tireGrip, tireFactor, surface,
|
|
68
|
+
longitudinalDemand: engine - braking, forwardVelocity: wheelU, lateralVelocity: wheelV,
|
|
69
|
+
});
|
|
70
|
+
const wheelFx = tire.longitudinal - rolling;
|
|
71
|
+
const bodyFx = wheelFx * cs - tire.lateral * sn;
|
|
72
|
+
const bodyFy = wheelFx * sn + tire.lateral * cs;
|
|
73
|
+
fx += bodyFx;
|
|
74
|
+
fy += bodyFy;
|
|
75
|
+
moment += x * bodyFy - y * bodyFx;
|
|
76
|
+
resistanceMoment += x * (-rolling * sn) - y * (-rolling * cs);
|
|
77
|
+
if (wheel.front) frontUsage = Math.max(frontUsage, tire.usage);
|
|
78
|
+
else rearUsage = Math.max(rearUsage, tire.usage);
|
|
79
|
+
}
|
|
80
|
+
const dragFactor = advancedDragFactor(car);
|
|
81
|
+
fx -= dragFactor * speed * u;
|
|
82
|
+
fy -= dragFactor * speed * v;
|
|
83
|
+
car.longitudinalAcceleration = fx / car.mass;
|
|
84
|
+
car.lateralAcceleration = fy / car.mass;
|
|
85
|
+
const ax = (fx * cos - fy * sin) / car.mass;
|
|
86
|
+
const ay = (fx * sin + fy * cos) / car.mass;
|
|
87
|
+
car.velocityX += ax * h * SIM_UNITS_PER_METER;
|
|
88
|
+
car.velocityY += ay * h * SIM_UNITS_PER_METER;
|
|
89
|
+
car.yawRate = yaw + moment / (car.mass * MODEL.yawInertiaPerMass) * h;
|
|
90
|
+
resistanceYawAcceleration = resistanceMoment / (car.mass * MODEL.yawInertiaPerMass);
|
|
91
|
+
car.heading = normalizeAngle(car.heading + car.yawRate * h);
|
|
92
|
+
car.speed = Math.hypot(car.velocityX, car.velocityY);
|
|
93
|
+
// Retain the package's supported top-speed envelope, including externally
|
|
94
|
+
// placed cars. Engine/drag balance determines normal acceleration below it.
|
|
95
|
+
if (car.speed > TOP_SPEED_SIM_UNITS_PER_SECOND) {
|
|
96
|
+
const scale = TOP_SPEED_SIM_UNITS_PER_SECOND / car.speed;
|
|
97
|
+
car.velocityX *= scale;
|
|
98
|
+
car.velocityY *= scale;
|
|
99
|
+
car.speed = TOP_SPEED_SIM_UNITS_PER_SECOND;
|
|
100
|
+
}
|
|
101
|
+
car.x += car.velocityX * h;
|
|
102
|
+
car.y += car.velocityY * h;
|
|
103
|
+
}
|
|
104
|
+
const ax = (car.velocityX / SIM_UNITS_PER_METER - initialVelocityX) / dt;
|
|
105
|
+
const ay = (car.velocityY / SIM_UNITS_PER_METER - initialVelocityY) / dt;
|
|
106
|
+
car.longitudinalAcceleration = ax * Math.cos(initialHeading) + ay * Math.sin(initialHeading);
|
|
107
|
+
car.lateralAcceleration = -ax * Math.sin(initialHeading) + ay * Math.cos(initialHeading);
|
|
108
|
+
car.longitudinalG = car.longitudinalAcceleration / MODEL.gravity;
|
|
109
|
+
car.lateralG = car.lateralAcceleration / MODEL.gravity;
|
|
110
|
+
car.gripUsage = Math.max(frontUsage, rearUsage);
|
|
111
|
+
car.slipAngleRadians = car.speed > 0.01 ? normalizeAngle(Math.atan2(car.velocityY, car.velocityX) - car.heading) : 0;
|
|
112
|
+
car.tractionLimited = car.gripUsage > 1;
|
|
113
|
+
car.stabilityState = Math.abs(car.slipAngleRadians) > 0.35 ? 'spin-risk'
|
|
114
|
+
: rearUsage > 1 && rearUsage > frontUsage * 1.15 ? 'oversteer'
|
|
115
|
+
: frontUsage > 1 ? 'understeer' : 'stable';
|
|
116
|
+
car.wheelDragYawRate = resistanceYawAcceleration * dt;
|
|
117
|
+
car.steerSaturation = 1 / Math.max(1, frontUsage);
|
|
118
|
+
car.turnRadius = Math.abs(car.yawRate) < 0.001 ? Infinity : car.speed / Math.abs(car.yawRate);
|
|
119
|
+
car.throttle = throttle;
|
|
120
|
+
car.brake = brake;
|
|
121
|
+
if (tireDegradationEnabled !== false) {
|
|
122
|
+
const care = clamp(Number(car.tireCare) || 1, 0.45, 1.8);
|
|
123
|
+
const wear = (0.035 + Math.abs(car.lateralG) * 0.11 + brake * 0.07 + throttle * 0.025 + Math.max(0, car.gripUsage - 1) * 0.03) / care;
|
|
124
|
+
car.tireEnergy = clamp((car.tireEnergy ?? 100) - wear * dt, 1, 100);
|
|
125
|
+
}
|
|
126
|
+
return car;
|
|
127
|
+
}
|
|
@@ -6,8 +6,9 @@ import {
|
|
|
6
6
|
} from '../collisionGeometry.js';
|
|
7
7
|
import { canCollide, isCollidable } from '../participants/participantInteractions.js';
|
|
8
8
|
import { clamp, normalizeAngle } from '../simMath.js';
|
|
9
|
-
import { VEHICLE_LIMITS } from './vehiclePhysics.js';
|
|
10
|
-
import { shiftPreviousRenderPose } from '
|
|
9
|
+
import { VEHICLE_LIMITS, isSimulatorPhysicsMode } from './vehiclePhysics.js';
|
|
10
|
+
import { shiftPreviousRenderPose } from './vehicleKinematics.js';
|
|
11
|
+
import { applyAdvancedContactResponse } from './advancedContactResponse.js';
|
|
11
12
|
|
|
12
13
|
const MAX_COLLISION_CORRECTION = 4.5;
|
|
13
14
|
|
|
@@ -113,23 +114,29 @@ export function resolveCollisionsForSimulation(sim) {
|
|
|
113
114
|
second.y += secondCorrectionY;
|
|
114
115
|
}
|
|
115
116
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
const advancedPhysics = isSimulatorPhysicsMode(sim.physicsMode);
|
|
118
|
+
if (advancedPhysics) {
|
|
119
|
+
applyContactVelocityResponse(sim, first, second, collision.axis, {
|
|
120
|
+
firstFixed: firstPitControlled,
|
|
121
|
+
secondFixed: secondPitControlled,
|
|
122
|
+
});
|
|
123
|
+
} else if (oneCarFixed) {
|
|
124
|
+
if (!firstPitControlled) dampPitContactVelocity(first, 0.985, isSimulatorPhysicsMode(sim.physicsMode));
|
|
125
|
+
if (!secondPitControlled) dampPitContactVelocity(second, 0.985, isSimulatorPhysicsMode(sim.physicsMode));
|
|
119
126
|
} else {
|
|
120
127
|
applyContactVelocityResponse(sim, first, second, collision.axis);
|
|
121
128
|
}
|
|
122
129
|
|
|
123
|
-
const yawNudge = clamp(collision.depth * 0.0025, 0.008, 0.035);
|
|
130
|
+
const yawNudge = advancedPhysics ? 0 : clamp(collision.depth * 0.0025, 0.008, 0.035);
|
|
124
131
|
const freshContact = first.contactCooldown <= 0 && second.contactCooldown <= 0;
|
|
125
132
|
const firstHeadingCorrection = firstPitControlled ? 0 : -collision.axis.y * yawNudge;
|
|
126
133
|
const secondHeadingCorrection = secondPitControlled ? 0 : collision.axis.y * yawNudge;
|
|
127
134
|
if (!firstPitControlled) {
|
|
128
|
-
first.heading = normalizeAngle(first.heading + firstHeadingCorrection);
|
|
135
|
+
if (!advancedPhysics) first.heading = normalizeAngle(first.heading + firstHeadingCorrection);
|
|
129
136
|
shiftPreviousRenderPose(first, firstCorrectionX, firstCorrectionY, firstHeadingCorrection);
|
|
130
137
|
}
|
|
131
138
|
if (!secondPitControlled) {
|
|
132
|
-
second.heading = normalizeAngle(second.heading + secondHeadingCorrection);
|
|
139
|
+
if (!advancedPhysics) second.heading = normalizeAngle(second.heading + secondHeadingCorrection);
|
|
133
140
|
shiftPreviousRenderPose(second, secondCorrectionX, secondCorrectionY, secondHeadingCorrection);
|
|
134
141
|
}
|
|
135
142
|
first.contactCooldown = 1;
|
|
@@ -166,8 +173,15 @@ export function resolveCollisionsForSimulation(sim) {
|
|
|
166
173
|
|
|
167
174
|
export function applyContactVelocityResponse(sim, first, second, axis, options = {}) {
|
|
168
175
|
recordContactVelocityResponse(options.stats);
|
|
176
|
+
if (isSimulatorPhysicsMode(sim.physicsMode)) {
|
|
177
|
+
applyAdvancedContactResponse(first, second, axis, {
|
|
178
|
+
restitution: sim.rules.collisionRestitution,
|
|
179
|
+
firstFixed: options.firstFixed,
|
|
180
|
+
secondFixed: options.secondFixed,
|
|
181
|
+
});
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
169
184
|
if (
|
|
170
|
-
sim.physicsMode === 'advanced' ||
|
|
171
185
|
Number.isFinite(first.velocityX) ||
|
|
172
186
|
Number.isFinite(second.velocityX)
|
|
173
187
|
) {
|
|
@@ -37,17 +37,6 @@ export function wheelOutsideFromOffsets(minimumSignedOffset, maximumSignedOffset
|
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
export function analyticWheelState(patch, centerState, track, trackLimit) {
|
|
41
|
-
return writeAnalyticWheelStateWithProjectedHalfWidth(
|
|
42
|
-
{},
|
|
43
|
-
patch,
|
|
44
|
-
centerState,
|
|
45
|
-
track,
|
|
46
|
-
trackLimit,
|
|
47
|
-
analyticWheelProjectedHalfWidth(patch, centerState),
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
40
|
export function writeAnalyticWheelState(target, patch, centerState, track, trackLimit) {
|
|
52
41
|
return writeAnalyticWheelStateWithProjectedHalfWidth(
|
|
53
42
|
target,
|
|
@@ -87,7 +76,7 @@ export function writeAnalyticWheelStateWithProjectedHalfWidth(
|
|
|
87
76
|
const state = sampledStates.length === 1 ? (sampledStates[0] ?? {}) : {};
|
|
88
77
|
sampledStates.length = 1;
|
|
89
78
|
sampledStates[0] = state;
|
|
90
|
-
writeStateFromSignedOffset(state,
|
|
79
|
+
writeStateFromSignedOffset(state, track, signedOffset);
|
|
91
80
|
|
|
92
81
|
target.id = patch.id;
|
|
93
82
|
target.x = patch.center.x;
|
|
@@ -107,7 +96,7 @@ export function writeAnalyticWheelStateWithProjectedHalfWidth(
|
|
|
107
96
|
return target;
|
|
108
97
|
}
|
|
109
98
|
|
|
110
|
-
function writeStateFromSignedOffset(target,
|
|
99
|
+
function writeStateFromSignedOffset(target, track, signedOffset) {
|
|
111
100
|
const crossTrackError = Math.abs(signedOffset);
|
|
112
101
|
const surface = mainTrackSurface(track, crossTrackError);
|
|
113
102
|
target.signedOffset = signedOffset;
|
|
@@ -38,10 +38,6 @@ export function canSkipConnectorPitChecks(track, geometry, centerState, epsilon
|
|
|
38
38
|
return true;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
export function pitPatchOffsetRange(patch, centerState) {
|
|
42
|
-
return patchPitOffsetRange(patch, centerState);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
41
|
export function pitPatchInsideRoad(patch, centerState, epsilon = 0.001) {
|
|
46
42
|
const roadWidth = Number(centerState?.pitLaneRoadWidth);
|
|
47
43
|
if (!centerState?.inPitLane || !Number.isFinite(roadWidth) || roadWidth <= 0) return false;
|
|
@@ -50,14 +46,6 @@ export function pitPatchInsideRoad(patch, centerState, epsilon = 0.001) {
|
|
|
50
46
|
return range.minimum >= -halfRoadWidth - epsilon && range.maximum <= halfRoadWidth + epsilon;
|
|
51
47
|
}
|
|
52
48
|
|
|
53
|
-
export function pitPatchOverlapsRoad(patch, centerState, epsilon = 0.001) {
|
|
54
|
-
const roadWidth = Number(centerState?.pitLaneRoadWidth);
|
|
55
|
-
if (!centerState?.inPitLane || !Number.isFinite(roadWidth) || roadWidth <= 0) return false;
|
|
56
|
-
const halfRoadWidth = roadWidth / 2;
|
|
57
|
-
const range = patchPitOffsetRange(patch, centerState);
|
|
58
|
-
return range.minimum <= halfRoadWidth + epsilon && range.maximum >= -halfRoadWidth - epsilon;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
49
|
export function writeAnalyticPitWheelState(target, patch, centerState) {
|
|
62
50
|
const range = patchPitOffsetRange(patch, centerState);
|
|
63
51
|
const signedOffset = centerState.mainTrackSignedOffset ?? centerState.signedOffset;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clearCurrentDrsState } from '../timing/drsState.js';
|
|
1
2
|
import {
|
|
2
3
|
VEHICLE_GEOMETRY,
|
|
3
4
|
vehicleAxes,
|
|
@@ -89,10 +90,7 @@ function destroyCarOnBarrier(sim, car) {
|
|
|
89
90
|
car.outOfRace = true;
|
|
90
91
|
markCarDnf(sim, car, { reason: 'barrier' });
|
|
91
92
|
car.canAttack = false;
|
|
92
|
-
car
|
|
93
|
-
car.drsEligible = false;
|
|
94
|
-
car.drsZoneId = null;
|
|
95
|
-
car.drsZoneEnabled = false;
|
|
93
|
+
clearCurrentDrsState(car);
|
|
96
94
|
freezeBarrierMotion(car, sim.physicsMode);
|
|
97
95
|
sim.events.unshift({
|
|
98
96
|
type: 'car-destroyed',
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { ensureOrientedRectCorners } from './vehicleGeometry.js';
|
|
2
|
+
import { analyticWheelProjectedHalfWidth, wheelFullyOutside, writeAnalyticWheelState } from './mainTrackWheelSurface.js';
|
|
3
|
+
import { pitPatchInsideRoad, writeAnalyticPitWheelState } from './pitWheelSurface.js';
|
|
4
|
+
import { worstState } from './surfacePriority.js';
|
|
5
|
+
import { resetWheelSummary, recordWheelSummary, finishWheelSummary } from './wheelSurfaceSummary.js';
|
|
6
|
+
import {
|
|
7
|
+
nearestTrackStateForCar,
|
|
8
|
+
queryHintedTrackStateForCar,
|
|
9
|
+
queryLocalSegmentTrackProjectionsForCar,
|
|
10
|
+
queryLocalSegmentTrackStateForCar,
|
|
11
|
+
queryLocalSegmentTrackStatesForCar,
|
|
12
|
+
} from '../track/trackStatePolicy.js';
|
|
13
|
+
|
|
14
|
+
const STRAIGHT_CONNECTOR_CURVATURE_EPSILON = 1e-8;
|
|
15
|
+
const MAIN_TRACK_PATCH_MARGIN = 0.001;
|
|
16
|
+
|
|
17
|
+
function writeFullWheelState(target, patch, sampledStates, trackLimit) {
|
|
18
|
+
const state = worstState(sampledStates);
|
|
19
|
+
let minimumSignedOffset = Infinity;
|
|
20
|
+
let maximumSignedOffset = -Infinity;
|
|
21
|
+
for (let index = 0; index < sampledStates.length; index += 1) {
|
|
22
|
+
const signedOffset = sampledStates[index].signedOffset;
|
|
23
|
+
if (signedOffset < minimumSignedOffset) minimumSignedOffset = signedOffset;
|
|
24
|
+
if (signedOffset > maximumSignedOffset) maximumSignedOffset = signedOffset;
|
|
25
|
+
}
|
|
26
|
+
const outside = wheelFullyOutside(sampledStates, trackLimit);
|
|
27
|
+
|
|
28
|
+
target.id = patch.id;
|
|
29
|
+
target.x = patch.center.x;
|
|
30
|
+
target.y = patch.center.y;
|
|
31
|
+
target.signedOffset = state.signedOffset;
|
|
32
|
+
target.crossTrackError = state.crossTrackError;
|
|
33
|
+
target.surface = state.surface;
|
|
34
|
+
target.onTrack = Boolean(state.onTrack);
|
|
35
|
+
target.inPitLane = Boolean(state.inPitLane);
|
|
36
|
+
target.pitLanePart = state.pitLanePart ?? null;
|
|
37
|
+
target.pitBoxId = state.pitBoxId ?? null;
|
|
38
|
+
target.minimumSignedOffset = minimumSignedOffset;
|
|
39
|
+
target.maximumSignedOffset = maximumSignedOffset;
|
|
40
|
+
target.fullyOutsideWhiteLine = outside.fullyOutsideWhiteLine;
|
|
41
|
+
target.outsideSide = outside.outsideSide;
|
|
42
|
+
target.sampledStates = sampledStates;
|
|
43
|
+
return target;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function queryConnectorSampleState(track, car, point, progressHint, centerState) {
|
|
47
|
+
const baseSegmentId = Number.isInteger(centerState?.segmentId) ? centerState.segmentId : null;
|
|
48
|
+
if (baseSegmentId == null) return queryHintedTrackStateForCar(track, car, point, progressHint);
|
|
49
|
+
return queryLocalSegmentTrackStateForCar(
|
|
50
|
+
track,
|
|
51
|
+
car,
|
|
52
|
+
point,
|
|
53
|
+
baseSegmentId,
|
|
54
|
+
progressHint,
|
|
55
|
+
{ skipPitOverrideInsideMainRoad: true },
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function sampleFullConnectorPatchInto(sampledStates, patch, track, car, progressHint, centerState) {
|
|
60
|
+
const corners = ensureOrientedRectCorners(patch);
|
|
61
|
+
sampledStates.length = 1 + corners.length;
|
|
62
|
+
sampledStates[0] = queryConnectorSampleState(track, car, patch.center, progressHint, centerState);
|
|
63
|
+
for (let index = 0; index < corners.length; index += 1) {
|
|
64
|
+
sampledStates[index + 1] = queryConnectorSampleState(track, car, corners[index], progressHint, centerState);
|
|
65
|
+
}
|
|
66
|
+
return sampledStates;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function sampleFullNearestPatchInto(sampledStates, patch, track, car) {
|
|
70
|
+
const corners = ensureOrientedRectCorners(patch);
|
|
71
|
+
sampledStates.length = 1 + corners.length;
|
|
72
|
+
sampledStates[0] = nearestTrackStateForCar(track, car, patch.center, car.progress);
|
|
73
|
+
for (let index = 0; index < corners.length; index += 1) {
|
|
74
|
+
sampledStates[index + 1] = nearestTrackStateForCar(track, car, corners[index], car.progress);
|
|
75
|
+
}
|
|
76
|
+
return sampledStates;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function writeConnectorWheelState(target, {
|
|
80
|
+
patch,
|
|
81
|
+
wheelCenterState,
|
|
82
|
+
centerState,
|
|
83
|
+
car,
|
|
84
|
+
track,
|
|
85
|
+
trackLimit,
|
|
86
|
+
progressHint,
|
|
87
|
+
sampledStates,
|
|
88
|
+
}) {
|
|
89
|
+
if (!wheelCenterState) {
|
|
90
|
+
return writeAnalyticWheelState(target, patch, centerState, track, trackLimit);
|
|
91
|
+
}
|
|
92
|
+
if (!wheelCenterState.inPitLane) {
|
|
93
|
+
return writeAnalyticWheelState(target, patch, wheelCenterState, track, trackLimit);
|
|
94
|
+
}
|
|
95
|
+
if (pitPatchInsideRoad(patch, wheelCenterState)) {
|
|
96
|
+
return writeAnalyticPitWheelState(target, patch, wheelCenterState);
|
|
97
|
+
}
|
|
98
|
+
sampleFullConnectorPatchInto(sampledStates, patch, track, car, progressHint, centerState);
|
|
99
|
+
return writeFullWheelState(target, patch, sampledStates, trackLimit);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function collectPatchCenters(target, patches) {
|
|
103
|
+
target.length = patches.length;
|
|
104
|
+
for (let index = 0; index < patches.length; index += 1) {
|
|
105
|
+
target[index] = patches[index].center;
|
|
106
|
+
}
|
|
107
|
+
return target;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function connectorWheelCenterStates(
|
|
111
|
+
track,
|
|
112
|
+
car,
|
|
113
|
+
patches,
|
|
114
|
+
centerState,
|
|
115
|
+
progressHint,
|
|
116
|
+
scratchStates = null,
|
|
117
|
+
scratchProjections = null,
|
|
118
|
+
scratchPositions = null,
|
|
119
|
+
) {
|
|
120
|
+
const baseSegmentId = Number.isInteger(centerState?.segmentId) ? centerState.segmentId : null;
|
|
121
|
+
const states = scratchStates ?? new Array(patches.length);
|
|
122
|
+
states.length = patches.length;
|
|
123
|
+
if (baseSegmentId == null) {
|
|
124
|
+
for (let index = 0; index < patches.length; index += 1) {
|
|
125
|
+
states[index] = queryHintedTrackStateForCar(track, car, patches[index].center, progressHint);
|
|
126
|
+
}
|
|
127
|
+
return states;
|
|
128
|
+
}
|
|
129
|
+
const positions = collectPatchCenters(scratchPositions ?? new Array(patches.length), patches);
|
|
130
|
+
return queryLocalSegmentTrackStatesForCar(
|
|
131
|
+
track,
|
|
132
|
+
car,
|
|
133
|
+
positions,
|
|
134
|
+
baseSegmentId,
|
|
135
|
+
progressHint,
|
|
136
|
+
{
|
|
137
|
+
radius: 2,
|
|
138
|
+
skipPitOverrideInsideMainRoad: true,
|
|
139
|
+
target: states,
|
|
140
|
+
projections: scratchProjections,
|
|
141
|
+
},
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function connectorWheelCenterProjections(
|
|
146
|
+
track,
|
|
147
|
+
car,
|
|
148
|
+
patches,
|
|
149
|
+
centerState,
|
|
150
|
+
progressHint,
|
|
151
|
+
scratchProjections = null,
|
|
152
|
+
scratchPositions = null,
|
|
153
|
+
) {
|
|
154
|
+
const baseSegmentId = Number.isInteger(centerState?.segmentId) ? centerState.segmentId : null;
|
|
155
|
+
if (baseSegmentId == null) return null;
|
|
156
|
+
const positions = collectPatchCenters(scratchPositions ?? new Array(patches.length), patches);
|
|
157
|
+
return queryLocalSegmentTrackProjectionsForCar(
|
|
158
|
+
track,
|
|
159
|
+
car,
|
|
160
|
+
positions,
|
|
161
|
+
baseSegmentId,
|
|
162
|
+
progressHint,
|
|
163
|
+
{
|
|
164
|
+
radius: 2,
|
|
165
|
+
projections: scratchProjections,
|
|
166
|
+
},
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function canUseStraightConnectorMainTrackAnalytic(track, geometry, centerState, trackLimit) {
|
|
171
|
+
if (
|
|
172
|
+
!track ||
|
|
173
|
+
!geometry?.contactPatches?.length ||
|
|
174
|
+
!centerState ||
|
|
175
|
+
centerState.inPitLane ||
|
|
176
|
+
!centerState.onTrack ||
|
|
177
|
+
Math.abs(centerState.curvature ?? 0) > STRAIGHT_CONNECTOR_CURVATURE_EPSILON
|
|
178
|
+
) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
for (let index = 0; index < geometry.contactPatches.length; index += 1) {
|
|
183
|
+
const patch = geometry.contactPatches[index];
|
|
184
|
+
const wheelCenterOffset =
|
|
185
|
+
(patch.center.x - centerState.x) * centerState.normalX +
|
|
186
|
+
(patch.center.y - centerState.y) * centerState.normalY;
|
|
187
|
+
const projectedHalfWidth = analyticWheelProjectedHalfWidth(patch, centerState);
|
|
188
|
+
if (
|
|
189
|
+
wheelCenterOffset - projectedHalfWidth < -trackLimit - MAIN_TRACK_PATCH_MARGIN ||
|
|
190
|
+
wheelCenterOffset + projectedHalfWidth > trackLimit + MAIN_TRACK_PATCH_MARGIN
|
|
191
|
+
) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function writeConnectorWheelStatesWithSummary({
|
|
199
|
+
targetWheels,
|
|
200
|
+
patches,
|
|
201
|
+
wheelCenterStates,
|
|
202
|
+
centerState,
|
|
203
|
+
car,
|
|
204
|
+
track,
|
|
205
|
+
trackLimit,
|
|
206
|
+
progressHint,
|
|
207
|
+
sampledStatePools,
|
|
208
|
+
summaryTarget = null,
|
|
209
|
+
}) {
|
|
210
|
+
const summary = resetWheelSummary(summaryTarget ?? {});
|
|
211
|
+
for (let index = 0; index < patches.length; index += 1) {
|
|
212
|
+
const sampledStates = sampledStatePools[index] ?? [];
|
|
213
|
+
sampledStatePools[index] = sampledStates;
|
|
214
|
+
const wheel = writeConnectorWheelState(targetWheels[index], {
|
|
215
|
+
patch: patches[index],
|
|
216
|
+
wheelCenterState: wheelCenterStates[index],
|
|
217
|
+
centerState,
|
|
218
|
+
car,
|
|
219
|
+
track,
|
|
220
|
+
trackLimit,
|
|
221
|
+
progressHint,
|
|
222
|
+
sampledStates,
|
|
223
|
+
});
|
|
224
|
+
recordWheelSummary(summary, wheel);
|
|
225
|
+
}
|
|
226
|
+
finishWheelSummary(summary, track, trackLimit);
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
wheels: targetWheels,
|
|
230
|
+
effectiveSurface: summary.effectiveSurface,
|
|
231
|
+
representative: summary.representative,
|
|
232
|
+
trackLimits: summary.trackLimits,
|
|
233
|
+
allOnTrackOrPit: summary.allOnTrackOrPit,
|
|
234
|
+
allInPitLane: summary.allInPitLane,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function writeNearestSampledWheelStatesWithSummary({
|
|
239
|
+
targetWheels,
|
|
240
|
+
patches,
|
|
241
|
+
car,
|
|
242
|
+
track,
|
|
243
|
+
trackLimit,
|
|
244
|
+
sampledStatePools,
|
|
245
|
+
summaryTarget = null,
|
|
246
|
+
}) {
|
|
247
|
+
const summary = resetWheelSummary(summaryTarget ?? {});
|
|
248
|
+
for (let index = 0; index < patches.length; index += 1) {
|
|
249
|
+
const patch = patches[index];
|
|
250
|
+
const sampledStates = sampledStatePools[index] ?? [];
|
|
251
|
+
sampledStatePools[index] = sampledStates;
|
|
252
|
+
sampleFullNearestPatchInto(sampledStates, patch, track, car);
|
|
253
|
+
const wheel = writeFullWheelState(targetWheels[index], patch, sampledStates, trackLimit);
|
|
254
|
+
recordWheelSummary(summary, wheel);
|
|
255
|
+
}
|
|
256
|
+
finishWheelSummary(summary, track, trackLimit);
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
wheels: targetWheels,
|
|
260
|
+
effectiveSurface: summary.effectiveSurface,
|
|
261
|
+
representative: summary.representative,
|
|
262
|
+
trackLimits: summary.trackLimits,
|
|
263
|
+
allOnTrackOrPit: summary.allOnTrackOrPit,
|
|
264
|
+
allInPitLane: summary.allInPitLane,
|
|
265
|
+
};
|
|
266
|
+
}
|