@inventure71/paddockjs 3.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +152 -1
- package/INSTALL_AND_UPDATE.md +21 -4
- package/README.md +103 -551
- package/docs/architecture.md +28 -18
- package/docs/component_inventory.md +5 -4
- package/docs/composable-layouts.md +79 -0
- package/docs/custom_model_controller.md +4 -4
- package/docs/data-helpers.md +50 -0
- package/docs/data_contract.md +13 -7
- package/docs/getting-started.md +105 -0
- package/docs/headless-environment.md +68 -0
- package/docs/index.md +17 -4
- package/docs/rules.md +6 -2
- package/docs/system_specs.md +60 -7
- package/docs/theming.md +64 -0
- package/docs/training.md +20 -14
- 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 +21 -5
- package/src/api/PaddockSimulatorController.js +78 -22
- package/src/api/runtimeTheme.js +59 -0
- package/src/app/F1SimulatorApp.js +24 -3
- package/src/app/readouts/timingTowerRenderer.js +203 -42
- package/src/app/runtime/frameLoop.js +5 -1
- package/src/config/defaultOptions.js +8 -1
- package/src/config/themeOptions.js +5 -0
- package/src/data/index.d.ts +212 -0
- package/src/data/index.js +14 -0
- package/src/environment/episode.js +11 -1
- package/src/environment/metrics.js +59 -16
- package/src/environment/observationVector.js +50 -33
- package/src/environment/observations.js +89 -20
- package/src/environment/runtime.js +41 -0
- 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 +130 -89
- package/src/environment/sensors/trackRays.js +115 -1
- package/src/index.d.ts +43 -0
- package/src/index.js +27 -2
- package/src/placeholder/index.d.ts +11 -0
- package/src/placeholder/index.js +61 -0
- package/src/placeholder.css +72 -0
- package/src/simulation/collisionGeometry.js +260 -96
- package/src/simulation/pit/pitFlow.js +21 -9
- package/src/simulation/pit/pitQueue.js +5 -3
- package/src/simulation/pit/pitRouteMovement.js +9 -4
- package/src/simulation/pit/pitRouting.js +77 -42
- 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 +2 -0
- package/src/simulation/race/raceStep.js +25 -13
- 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 +115 -36
- 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 +23 -0
- package/src/simulation/track/pitLaneState.js +218 -19
- package/src/simulation/track/spatialQueries.js +67 -11
- package/src/simulation/track/trackMath.js +91 -24
- package/src/simulation/track/trackModel.js +2 -0
- package/src/simulation/track/trackQueryIndex.js +1334 -100
- 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 -1
- package/src/simulation/vehicle/contactResolution.js +96 -59
- 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/vehiclePhysics.js +42 -27
- package/src/simulation/vehicle/vehicleSnapshots.js +23 -0
- package/src/simulation/vehicle/vehicleState.js +6 -0
- package/src/simulation/vehicle/wheelSurface.js +807 -92
- package/src/styles.css +497 -497
- package/src/ui/raceControlsTemplate.js +1 -0
- package/src/ui/telemetryDrawerTemplate.js +1 -0
- package/src/ui/telemetryTemplates.js +1 -0
|
@@ -1,27 +1,383 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { ensureOrientedRectCorners, getCurrentVehicleGeometryState } from './vehicleGeometry.js';
|
|
2
|
+
import {
|
|
3
|
+
analyticWheelProjectedHalfWidth,
|
|
4
|
+
wheelFullyOutside,
|
|
5
|
+
writeAnalyticWheelState,
|
|
6
|
+
writeAnalyticWheelStateWithProjectedHalfWidth,
|
|
7
|
+
} from './mainTrackWheelSurface.js';
|
|
8
|
+
import {
|
|
9
|
+
canUseAnalyticPitWheels,
|
|
10
|
+
canSkipConnectorPitChecks,
|
|
11
|
+
isNearPitConnector,
|
|
12
|
+
pitPatchInsideRoad,
|
|
13
|
+
writeAnalyticPitWheelState,
|
|
14
|
+
} from './pitWheelSurface.js';
|
|
15
|
+
import { getEffectiveSurface, priority, worstState } from './surfacePriority.js';
|
|
16
|
+
import {
|
|
17
|
+
nearestTrackStateForCar,
|
|
18
|
+
pitOverrideAllowedForCar,
|
|
19
|
+
queryHintedTrackStateForCar,
|
|
20
|
+
queryLocalSegmentTrackProjectionsForCar,
|
|
21
|
+
queryLocalSegmentTrackStateForCar,
|
|
22
|
+
queryLocalSegmentTrackStatesForCar,
|
|
23
|
+
queryRunoffTrackStateForCar,
|
|
24
|
+
writeLocalSegmentTrackStatesFromProjections,
|
|
25
|
+
} from '../track/trackStatePolicy.js';
|
|
6
26
|
|
|
7
|
-
|
|
8
|
-
|
|
27
|
+
export { getEffectiveSurface } from './surfacePriority.js';
|
|
28
|
+
|
|
29
|
+
const STRAIGHT_CONNECTOR_CURVATURE_EPSILON = 1e-8;
|
|
30
|
+
const MAIN_TRACK_PATCH_MARGIN = 0.001;
|
|
31
|
+
|
|
32
|
+
function usesAutomaticCenterState(centerState, cacheAsAuto = false) {
|
|
33
|
+
return cacheAsAuto || centerState == null;
|
|
9
34
|
}
|
|
10
35
|
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
return
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
centerState.
|
|
18
|
-
centerState.
|
|
19
|
-
centerState.
|
|
20
|
-
centerState.
|
|
21
|
-
|
|
36
|
+
function cacheMatchesCenterState(cache, centerState, cacheAsAuto = false) {
|
|
37
|
+
const automatic = usesAutomaticCenterState(centerState, cacheAsAuto);
|
|
38
|
+
if ((cache.centerStateAuto ?? true) !== automatic) return false;
|
|
39
|
+
if (automatic) return true;
|
|
40
|
+
return cache.centerStateDistance === centerState.distance &&
|
|
41
|
+
cache.centerStateSignedOffset === centerState.signedOffset &&
|
|
42
|
+
cache.centerStateCrossTrackError === centerState.crossTrackError &&
|
|
43
|
+
cache.centerStateSurface === (centerState.surface ?? null) &&
|
|
44
|
+
cache.centerStateInPitLane === Boolean(centerState.inPitLane) &&
|
|
45
|
+
cache.centerStatePitLanePart === (centerState.pitLanePart ?? null) &&
|
|
46
|
+
cache.centerStatePitBoxId === (centerState.pitBoxId ?? null);
|
|
22
47
|
}
|
|
23
48
|
|
|
24
|
-
|
|
49
|
+
function writeCachedCenterState(cache, centerState, cacheAsAuto = false) {
|
|
50
|
+
const automatic = usesAutomaticCenterState(centerState, cacheAsAuto);
|
|
51
|
+
cache.centerStateAuto = automatic;
|
|
52
|
+
if (automatic) {
|
|
53
|
+
cache.centerStateDistance = null;
|
|
54
|
+
cache.centerStateSignedOffset = null;
|
|
55
|
+
cache.centerStateCrossTrackError = null;
|
|
56
|
+
cache.centerStateSurface = null;
|
|
57
|
+
cache.centerStateInPitLane = false;
|
|
58
|
+
cache.centerStatePitLanePart = null;
|
|
59
|
+
cache.centerStatePitBoxId = null;
|
|
60
|
+
return cache;
|
|
61
|
+
}
|
|
62
|
+
cache.centerStateDistance = centerState.distance;
|
|
63
|
+
cache.centerStateSignedOffset = centerState.signedOffset;
|
|
64
|
+
cache.centerStateCrossTrackError = centerState.crossTrackError;
|
|
65
|
+
cache.centerStateSurface = centerState.surface ?? null;
|
|
66
|
+
cache.centerStateInPitLane = Boolean(centerState.inPitLane);
|
|
67
|
+
cache.centerStatePitLanePart = centerState.pitLanePart ?? null;
|
|
68
|
+
cache.centerStatePitBoxId = centerState.pitBoxId ?? null;
|
|
69
|
+
return cache;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function canRefreshAutomaticCenterStateFromTrackState(car, track) {
|
|
73
|
+
const trackState = car?.trackState;
|
|
74
|
+
if (!trackState || trackState.inPitLane) return false;
|
|
75
|
+
if (
|
|
76
|
+
car?.trackStatePoseX !== car?.x ||
|
|
77
|
+
car?.trackStatePoseY !== car?.y ||
|
|
78
|
+
car?.trackStatePoseHeading !== car?.heading
|
|
79
|
+
) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
const mainRoadEdge = track.width / 2 + (track.kerbWidth ?? 0);
|
|
83
|
+
return Number.isFinite(trackState.crossTrackError) && trackState.crossTrackError <= mainRoadEdge;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function writeRepresentativeTrackState(target, centerState, effectiveSurface, representative, onTrack, inPitLane) {
|
|
87
|
+
target.segmentId = centerState.segmentId;
|
|
88
|
+
target.x = centerState.x;
|
|
89
|
+
target.y = centerState.y;
|
|
90
|
+
target.distance = centerState.distance;
|
|
91
|
+
target.heading = centerState.heading;
|
|
92
|
+
target.normalX = centerState.normalX;
|
|
93
|
+
target.normalY = centerState.normalY;
|
|
94
|
+
target.curvature = centerState.curvature;
|
|
95
|
+
target.signedOffset = representative.signedOffset;
|
|
96
|
+
target.crossTrackError = representative.crossTrackError;
|
|
97
|
+
target.surface = effectiveSurface;
|
|
98
|
+
target.onTrack = onTrack;
|
|
99
|
+
target.distanceSquared = centerState.distanceSquared;
|
|
100
|
+
target.inPitLane = inPitLane;
|
|
101
|
+
target.pitLanePart = centerState.pitLanePart;
|
|
102
|
+
target.pitBoxId = centerState.pitBoxId;
|
|
103
|
+
target.mainTrackSignedOffset = centerState.mainTrackSignedOffset;
|
|
104
|
+
target.mainTrackCrossTrackError = centerState.mainTrackCrossTrackError;
|
|
105
|
+
target.pitLaneCrossTrackError = centerState.pitLaneCrossTrackError;
|
|
106
|
+
target.wheelSurface = effectiveSurface;
|
|
107
|
+
return target;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function prepareScratchWheels(scratch, count) {
|
|
111
|
+
if (!scratch) return null;
|
|
112
|
+
const wheels = scratch.wheels ?? [];
|
|
113
|
+
const wheelPool = scratch.wheelPool ?? [];
|
|
114
|
+
scratch.wheels = wheels;
|
|
115
|
+
scratch.wheelPool = wheelPool;
|
|
116
|
+
wheels.length = count;
|
|
117
|
+
for (let index = 0; index < count; index += 1) {
|
|
118
|
+
const wheel = wheelPool[index] ?? {};
|
|
119
|
+
wheelPool[index] = wheel;
|
|
120
|
+
wheels[index] = wheel;
|
|
121
|
+
}
|
|
122
|
+
return wheels;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function recordScratchlessScalarWheelBatch(car, wheelCount) {
|
|
126
|
+
const stats = car?.runtimeBenchmarkStats;
|
|
127
|
+
if (!stats) return;
|
|
128
|
+
stats.scratchlessScalarWheelBatches = (stats.scratchlessScalarWheelBatches ?? 0) + 1;
|
|
129
|
+
stats.scratchlessScalarWheelWrites = (stats.scratchlessScalarWheelWrites ?? 0) + wheelCount;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function createWheelTargets(count) {
|
|
133
|
+
const wheels = new Array(count);
|
|
134
|
+
for (let index = 0; index < count; index += 1) {
|
|
135
|
+
wheels[index] = {};
|
|
136
|
+
}
|
|
137
|
+
return wheels;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function writeFullWheelState(target, patch, sampledStates, trackLimit) {
|
|
141
|
+
const state = worstState(sampledStates);
|
|
142
|
+
let minimumSignedOffset = Infinity;
|
|
143
|
+
let maximumSignedOffset = -Infinity;
|
|
144
|
+
for (let index = 0; index < sampledStates.length; index += 1) {
|
|
145
|
+
const signedOffset = sampledStates[index].signedOffset;
|
|
146
|
+
if (signedOffset < minimumSignedOffset) minimumSignedOffset = signedOffset;
|
|
147
|
+
if (signedOffset > maximumSignedOffset) maximumSignedOffset = signedOffset;
|
|
148
|
+
}
|
|
149
|
+
const outside = wheelFullyOutside(sampledStates, trackLimit);
|
|
150
|
+
|
|
151
|
+
target.id = patch.id;
|
|
152
|
+
target.x = patch.center.x;
|
|
153
|
+
target.y = patch.center.y;
|
|
154
|
+
target.signedOffset = state.signedOffset;
|
|
155
|
+
target.crossTrackError = state.crossTrackError;
|
|
156
|
+
target.surface = state.surface;
|
|
157
|
+
target.onTrack = Boolean(state.onTrack);
|
|
158
|
+
target.inPitLane = Boolean(state.inPitLane);
|
|
159
|
+
target.pitLanePart = state.pitLanePart ?? null;
|
|
160
|
+
target.pitBoxId = state.pitBoxId ?? null;
|
|
161
|
+
target.minimumSignedOffset = minimumSignedOffset;
|
|
162
|
+
target.maximumSignedOffset = maximumSignedOffset;
|
|
163
|
+
target.fullyOutsideWhiteLine = outside.fullyOutsideWhiteLine;
|
|
164
|
+
target.outsideSide = outside.outsideSide;
|
|
165
|
+
target.sampledStates = sampledStates;
|
|
166
|
+
return target;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function resetWheelSummary(summary) {
|
|
170
|
+
const trackLimits = summary.trackLimits ?? {
|
|
171
|
+
violating: false,
|
|
172
|
+
side: 0,
|
|
173
|
+
outsideBy: 0,
|
|
174
|
+
};
|
|
175
|
+
summary.effectiveSurface = 'track';
|
|
176
|
+
summary.effectivePriority = priority(summary.effectiveSurface);
|
|
177
|
+
summary.representative = null;
|
|
178
|
+
summary.allRightOutside = true;
|
|
179
|
+
summary.allLeftOutside = true;
|
|
180
|
+
summary.allOnTrackOrPit = true;
|
|
181
|
+
summary.allInPitLane = true;
|
|
182
|
+
summary.minimumSignedOffset = Infinity;
|
|
183
|
+
summary.maximumSignedOffset = -Infinity;
|
|
184
|
+
summary.trackLimits = trackLimits;
|
|
185
|
+
return summary;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function prepareWheelSummary(scratch) {
|
|
189
|
+
if (!scratch) return null;
|
|
190
|
+
const summary = scratch.summary ?? {};
|
|
191
|
+
scratch.summary = summary;
|
|
192
|
+
return resetWheelSummary(summary);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function recordWheelSummary(summary, wheel) {
|
|
196
|
+
const wheelPriority = priority(wheel.surface);
|
|
197
|
+
if (wheelPriority > summary.effectivePriority) {
|
|
198
|
+
summary.effectivePriority = wheelPriority;
|
|
199
|
+
summary.effectiveSurface = wheel.surface;
|
|
200
|
+
}
|
|
201
|
+
if (!summary.representative || Math.abs(wheel.signedOffset) > Math.abs(summary.representative.signedOffset)) {
|
|
202
|
+
summary.representative = wheel;
|
|
203
|
+
}
|
|
204
|
+
if (wheel.outsideSide !== 1) summary.allRightOutside = false;
|
|
205
|
+
if (wheel.outsideSide !== -1) summary.allLeftOutside = false;
|
|
206
|
+
if (!(wheel.onTrack || wheel.inPitLane)) summary.allOnTrackOrPit = false;
|
|
207
|
+
if (!wheel.inPitLane) summary.allInPitLane = false;
|
|
208
|
+
if (wheel.minimumSignedOffset < summary.minimumSignedOffset) summary.minimumSignedOffset = wheel.minimumSignedOffset;
|
|
209
|
+
if (wheel.maximumSignedOffset > summary.maximumSignedOffset) summary.maximumSignedOffset = wheel.maximumSignedOffset;
|
|
210
|
+
return summary;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function finishWheelSummary(summary, track, trackLimit = track?.width / 2) {
|
|
214
|
+
const trackLimits = summary.trackLimits;
|
|
215
|
+
trackLimits.violating = false;
|
|
216
|
+
trackLimits.side = 0;
|
|
217
|
+
trackLimits.outsideBy = 0;
|
|
218
|
+
if (track && (summary.allRightOutside || summary.allLeftOutside)) {
|
|
219
|
+
const side = summary.allRightOutside ? 1 : -1;
|
|
220
|
+
const outsideBy = summary.allRightOutside
|
|
221
|
+
? summary.minimumSignedOffset - trackLimit
|
|
222
|
+
: -trackLimit - summary.maximumSignedOffset;
|
|
223
|
+
trackLimits.violating = outsideBy > 0;
|
|
224
|
+
trackLimits.side = side;
|
|
225
|
+
trackLimits.outsideBy = Math.max(0, outsideBy);
|
|
226
|
+
}
|
|
227
|
+
return summary;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function queryConnectorSampleState(track, car, point, progressHint, centerState) {
|
|
231
|
+
const baseSegmentId = Number.isInteger(centerState?.segmentId) ? centerState.segmentId : null;
|
|
232
|
+
if (baseSegmentId == null) return queryHintedTrackStateForCar(track, car, point, progressHint);
|
|
233
|
+
return queryLocalSegmentTrackStateForCar(
|
|
234
|
+
track,
|
|
235
|
+
car,
|
|
236
|
+
point,
|
|
237
|
+
baseSegmentId,
|
|
238
|
+
progressHint,
|
|
239
|
+
{ skipPitOverrideInsideMainRoad: true },
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function sampleFullConnectorPatchInto(sampledStates, patch, track, car, progressHint, centerState) {
|
|
244
|
+
const corners = ensureOrientedRectCorners(patch);
|
|
245
|
+
sampledStates.length = 1 + corners.length;
|
|
246
|
+
sampledStates[0] = queryConnectorSampleState(track, car, patch.center, progressHint, centerState);
|
|
247
|
+
for (let index = 0; index < corners.length; index += 1) {
|
|
248
|
+
sampledStates[index + 1] = queryConnectorSampleState(track, car, corners[index], progressHint, centerState);
|
|
249
|
+
}
|
|
250
|
+
return sampledStates;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function sampleFullNearestPatchInto(sampledStates, patch, track, car) {
|
|
254
|
+
const corners = ensureOrientedRectCorners(patch);
|
|
255
|
+
sampledStates.length = 1 + corners.length;
|
|
256
|
+
sampledStates[0] = nearestTrackStateForCar(track, car, patch.center, car.progress);
|
|
257
|
+
for (let index = 0; index < corners.length; index += 1) {
|
|
258
|
+
sampledStates[index + 1] = nearestTrackStateForCar(track, car, corners[index], car.progress);
|
|
259
|
+
}
|
|
260
|
+
return sampledStates;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function writeConnectorWheelState(target, {
|
|
264
|
+
patch,
|
|
265
|
+
wheelCenterState,
|
|
266
|
+
centerState,
|
|
267
|
+
car,
|
|
268
|
+
track,
|
|
269
|
+
trackLimit,
|
|
270
|
+
progressHint,
|
|
271
|
+
sampledStates,
|
|
272
|
+
}) {
|
|
273
|
+
if (!wheelCenterState) {
|
|
274
|
+
return writeAnalyticWheelState(target, patch, centerState, track, trackLimit);
|
|
275
|
+
}
|
|
276
|
+
if (!wheelCenterState.inPitLane) {
|
|
277
|
+
return writeAnalyticWheelState(target, patch, wheelCenterState, track, trackLimit);
|
|
278
|
+
}
|
|
279
|
+
if (pitPatchInsideRoad(patch, wheelCenterState)) {
|
|
280
|
+
return writeAnalyticPitWheelState(target, patch, wheelCenterState);
|
|
281
|
+
}
|
|
282
|
+
sampleFullConnectorPatchInto(sampledStates, patch, track, car, progressHint, centerState);
|
|
283
|
+
return writeFullWheelState(target, patch, sampledStates, trackLimit);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function collectPatchCenters(target, patches) {
|
|
287
|
+
target.length = patches.length;
|
|
288
|
+
for (let index = 0; index < patches.length; index += 1) {
|
|
289
|
+
target[index] = patches[index].center;
|
|
290
|
+
}
|
|
291
|
+
return target;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function connectorWheelCenterStates(
|
|
295
|
+
track,
|
|
296
|
+
car,
|
|
297
|
+
patches,
|
|
298
|
+
centerState,
|
|
299
|
+
progressHint,
|
|
300
|
+
scratchStates = null,
|
|
301
|
+
scratchProjections = null,
|
|
302
|
+
scratchPositions = null,
|
|
303
|
+
) {
|
|
304
|
+
const baseSegmentId = Number.isInteger(centerState?.segmentId) ? centerState.segmentId : null;
|
|
305
|
+
const states = scratchStates ?? new Array(patches.length);
|
|
306
|
+
states.length = patches.length;
|
|
307
|
+
if (baseSegmentId == null) {
|
|
308
|
+
for (let index = 0; index < patches.length; index += 1) {
|
|
309
|
+
states[index] = queryHintedTrackStateForCar(track, car, patches[index].center, progressHint);
|
|
310
|
+
}
|
|
311
|
+
return states;
|
|
312
|
+
}
|
|
313
|
+
const positions = collectPatchCenters(scratchPositions ?? new Array(patches.length), patches);
|
|
314
|
+
return queryLocalSegmentTrackStatesForCar(
|
|
315
|
+
track,
|
|
316
|
+
car,
|
|
317
|
+
positions,
|
|
318
|
+
baseSegmentId,
|
|
319
|
+
progressHint,
|
|
320
|
+
{
|
|
321
|
+
radius: 2,
|
|
322
|
+
skipPitOverrideInsideMainRoad: true,
|
|
323
|
+
target: states,
|
|
324
|
+
projections: scratchProjections,
|
|
325
|
+
},
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function connectorWheelCenterProjections(
|
|
330
|
+
track,
|
|
331
|
+
car,
|
|
332
|
+
patches,
|
|
333
|
+
centerState,
|
|
334
|
+
progressHint,
|
|
335
|
+
scratchProjections = null,
|
|
336
|
+
scratchPositions = null,
|
|
337
|
+
) {
|
|
338
|
+
const baseSegmentId = Number.isInteger(centerState?.segmentId) ? centerState.segmentId : null;
|
|
339
|
+
if (baseSegmentId == null) return null;
|
|
340
|
+
const positions = collectPatchCenters(scratchPositions ?? new Array(patches.length), patches);
|
|
341
|
+
return queryLocalSegmentTrackProjectionsForCar(
|
|
342
|
+
track,
|
|
343
|
+
car,
|
|
344
|
+
positions,
|
|
345
|
+
baseSegmentId,
|
|
346
|
+
progressHint,
|
|
347
|
+
{
|
|
348
|
+
radius: 2,
|
|
349
|
+
projections: scratchProjections,
|
|
350
|
+
},
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function canUseStraightConnectorMainTrackAnalytic(track, geometry, centerState, trackLimit) {
|
|
355
|
+
if (
|
|
356
|
+
!track ||
|
|
357
|
+
!geometry?.contactPatches?.length ||
|
|
358
|
+
!centerState ||
|
|
359
|
+
centerState.inPitLane ||
|
|
360
|
+
!centerState.onTrack ||
|
|
361
|
+
Math.abs(centerState.curvature ?? 0) > STRAIGHT_CONNECTOR_CURVATURE_EPSILON
|
|
362
|
+
) {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
for (let index = 0; index < geometry.contactPatches.length; index += 1) {
|
|
367
|
+
const patch = geometry.contactPatches[index];
|
|
368
|
+
const wheelCenterOffset =
|
|
369
|
+
(patch.center.x - centerState.x) * centerState.normalX +
|
|
370
|
+
(patch.center.y - centerState.y) * centerState.normalY;
|
|
371
|
+
const projectedHalfWidth = analyticWheelProjectedHalfWidth(patch, centerState);
|
|
372
|
+
if (
|
|
373
|
+
wheelCenterOffset - projectedHalfWidth < -trackLimit - MAIN_TRACK_PATCH_MARGIN ||
|
|
374
|
+
wheelCenterOffset + projectedHalfWidth > trackLimit + MAIN_TRACK_PATCH_MARGIN
|
|
375
|
+
) {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
25
381
|
|
|
26
382
|
export function isWholeCarOutsideTrackLimits(wheels = [], track, relaxedMargin = 0) {
|
|
27
383
|
if (!wheels.length || !track) {
|
|
@@ -32,8 +388,17 @@ export function isWholeCarOutsideTrackLimits(wheels = [], track, relaxedMargin =
|
|
|
32
388
|
};
|
|
33
389
|
}
|
|
34
390
|
const trackLimit = track.width / 2 + relaxedMargin;
|
|
35
|
-
|
|
36
|
-
|
|
391
|
+
let right = true;
|
|
392
|
+
let left = true;
|
|
393
|
+
let minimumSignedOffset = Infinity;
|
|
394
|
+
let maximumSignedOffset = -Infinity;
|
|
395
|
+
for (let index = 0; index < wheels.length; index += 1) {
|
|
396
|
+
const wheel = wheels[index];
|
|
397
|
+
if (wheel.outsideSide !== 1) right = false;
|
|
398
|
+
if (wheel.outsideSide !== -1) left = false;
|
|
399
|
+
if (wheel.minimumSignedOffset < minimumSignedOffset) minimumSignedOffset = wheel.minimumSignedOffset;
|
|
400
|
+
if (wheel.maximumSignedOffset > maximumSignedOffset) maximumSignedOffset = wheel.maximumSignedOffset;
|
|
401
|
+
}
|
|
37
402
|
if (!right && !left) {
|
|
38
403
|
return {
|
|
39
404
|
violating: false,
|
|
@@ -43,8 +408,8 @@ export function isWholeCarOutsideTrackLimits(wheels = [], track, relaxedMargin =
|
|
|
43
408
|
}
|
|
44
409
|
const side = right ? 1 : -1;
|
|
45
410
|
const outsideBy = right
|
|
46
|
-
?
|
|
47
|
-
: -trackLimit -
|
|
411
|
+
? minimumSignedOffset - trackLimit
|
|
412
|
+
: -trackLimit - maximumSignedOffset;
|
|
48
413
|
|
|
49
414
|
return {
|
|
50
415
|
violating: outsideBy > 0,
|
|
@@ -53,82 +418,419 @@ export function isWholeCarOutsideTrackLimits(wheels = [], track, relaxedMargin =
|
|
|
53
418
|
};
|
|
54
419
|
}
|
|
55
420
|
|
|
56
|
-
|
|
57
|
-
const
|
|
421
|
+
function writeAnalyticWheelStatesWithSummary(targetWheels, patches, centerState, track, trackLimit, summaryTarget = null) {
|
|
422
|
+
const sharedProjectedHalfWidth = patches.length > 0
|
|
423
|
+
? analyticWheelProjectedHalfWidth(patches[0], centerState)
|
|
424
|
+
: 0;
|
|
425
|
+
const summary = resetWheelSummary(summaryTarget ?? {});
|
|
426
|
+
|
|
427
|
+
for (let index = 0; index < patches.length; index += 1) {
|
|
428
|
+
const wheel = writeAnalyticWheelStateWithProjectedHalfWidth(
|
|
429
|
+
targetWheels[index],
|
|
430
|
+
patches[index],
|
|
431
|
+
centerState,
|
|
432
|
+
track,
|
|
433
|
+
trackLimit,
|
|
434
|
+
sharedProjectedHalfWidth,
|
|
435
|
+
);
|
|
436
|
+
recordWheelSummary(summary, wheel);
|
|
437
|
+
}
|
|
438
|
+
finishWheelSummary(summary, track, trackLimit);
|
|
439
|
+
|
|
440
|
+
return {
|
|
441
|
+
wheels: targetWheels,
|
|
442
|
+
effectiveSurface: summary.effectiveSurface,
|
|
443
|
+
representative: summary.representative,
|
|
444
|
+
trackLimits: summary.trackLimits,
|
|
445
|
+
allOnTrackOrPit: summary.allOnTrackOrPit,
|
|
446
|
+
allInPitLane: summary.allInPitLane,
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function writeAnalyticPitWheelStatesWithSummary(targetWheels, patches, centerState, track, trackLimit, summaryTarget = null) {
|
|
451
|
+
const summary = resetWheelSummary(summaryTarget ?? {});
|
|
452
|
+
|
|
453
|
+
for (let index = 0; index < patches.length; index += 1) {
|
|
454
|
+
const wheel = writeAnalyticPitWheelState(targetWheels[index], patches[index], centerState);
|
|
455
|
+
recordWheelSummary(summary, wheel);
|
|
456
|
+
}
|
|
457
|
+
finishWheelSummary(summary, track, trackLimit);
|
|
458
|
+
|
|
459
|
+
return {
|
|
460
|
+
wheels: targetWheels,
|
|
461
|
+
effectiveSurface: summary.effectiveSurface,
|
|
462
|
+
representative: summary.representative,
|
|
463
|
+
trackLimits: summary.trackLimits,
|
|
464
|
+
allOnTrackOrPit: summary.allOnTrackOrPit,
|
|
465
|
+
allInPitLane: summary.allInPitLane,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function writeAnalyticWheelStatesFromTrackStatesWithSummary(
|
|
470
|
+
targetWheels,
|
|
471
|
+
patches,
|
|
472
|
+
wheelTrackStates,
|
|
473
|
+
track,
|
|
474
|
+
trackLimit,
|
|
475
|
+
summaryTarget = null,
|
|
476
|
+
) {
|
|
477
|
+
const summary = resetWheelSummary(summaryTarget ?? {});
|
|
478
|
+
|
|
479
|
+
for (let index = 0; index < patches.length; index += 1) {
|
|
480
|
+
const wheel = writeAnalyticWheelState(
|
|
481
|
+
targetWheels[index],
|
|
482
|
+
patches[index],
|
|
483
|
+
wheelTrackStates[index],
|
|
484
|
+
track,
|
|
485
|
+
trackLimit,
|
|
486
|
+
);
|
|
487
|
+
recordWheelSummary(summary, wheel);
|
|
488
|
+
}
|
|
489
|
+
finishWheelSummary(summary, track, trackLimit);
|
|
490
|
+
|
|
491
|
+
return {
|
|
492
|
+
wheels: targetWheels,
|
|
493
|
+
effectiveSurface: summary.effectiveSurface,
|
|
494
|
+
representative: summary.representative,
|
|
495
|
+
trackLimits: summary.trackLimits,
|
|
496
|
+
allOnTrackOrPit: summary.allOnTrackOrPit,
|
|
497
|
+
allInPitLane: summary.allInPitLane,
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function writeConnectorWheelStatesWithSummary({
|
|
502
|
+
targetWheels,
|
|
503
|
+
patches,
|
|
504
|
+
wheelCenterStates,
|
|
505
|
+
centerState,
|
|
506
|
+
car,
|
|
507
|
+
track,
|
|
508
|
+
trackLimit,
|
|
509
|
+
progressHint,
|
|
510
|
+
sampledStatePools,
|
|
511
|
+
summaryTarget = null,
|
|
512
|
+
}) {
|
|
513
|
+
const summary = resetWheelSummary(summaryTarget ?? {});
|
|
514
|
+
for (let index = 0; index < patches.length; index += 1) {
|
|
515
|
+
const sampledStates = sampledStatePools[index] ?? [];
|
|
516
|
+
sampledStatePools[index] = sampledStates;
|
|
517
|
+
const wheel = writeConnectorWheelState(targetWheels[index], {
|
|
518
|
+
patch: patches[index],
|
|
519
|
+
wheelCenterState: wheelCenterStates[index],
|
|
520
|
+
centerState,
|
|
521
|
+
car,
|
|
522
|
+
track,
|
|
523
|
+
trackLimit,
|
|
524
|
+
progressHint,
|
|
525
|
+
sampledStates,
|
|
526
|
+
});
|
|
527
|
+
recordWheelSummary(summary, wheel);
|
|
528
|
+
}
|
|
529
|
+
finishWheelSummary(summary, track, trackLimit);
|
|
530
|
+
|
|
531
|
+
return {
|
|
532
|
+
wheels: targetWheels,
|
|
533
|
+
effectiveSurface: summary.effectiveSurface,
|
|
534
|
+
representative: summary.representative,
|
|
535
|
+
trackLimits: summary.trackLimits,
|
|
536
|
+
allOnTrackOrPit: summary.allOnTrackOrPit,
|
|
537
|
+
allInPitLane: summary.allInPitLane,
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function writeNearestSampledWheelStatesWithSummary({
|
|
542
|
+
targetWheels,
|
|
543
|
+
patches,
|
|
544
|
+
car,
|
|
545
|
+
track,
|
|
546
|
+
trackLimit,
|
|
547
|
+
sampledStatePools,
|
|
548
|
+
summaryTarget = null,
|
|
549
|
+
}) {
|
|
550
|
+
const summary = resetWheelSummary(summaryTarget ?? {});
|
|
551
|
+
for (let index = 0; index < patches.length; index += 1) {
|
|
552
|
+
const patch = patches[index];
|
|
553
|
+
const sampledStates = sampledStatePools[index] ?? [];
|
|
554
|
+
sampledStatePools[index] = sampledStates;
|
|
555
|
+
sampleFullNearestPatchInto(sampledStates, patch, track, car);
|
|
556
|
+
const wheel = writeFullWheelState(targetWheels[index], patch, sampledStates, trackLimit);
|
|
557
|
+
recordWheelSummary(summary, wheel);
|
|
558
|
+
}
|
|
559
|
+
finishWheelSummary(summary, track, trackLimit);
|
|
560
|
+
|
|
561
|
+
return {
|
|
562
|
+
wheels: targetWheels,
|
|
563
|
+
effectiveSurface: summary.effectiveSurface,
|
|
564
|
+
representative: summary.representative,
|
|
565
|
+
trackLimits: summary.trackLimits,
|
|
566
|
+
allOnTrackOrPit: summary.allOnTrackOrPit,
|
|
567
|
+
allInPitLane: summary.allInPitLane,
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
export function calculateWheelSurfaceState({
|
|
572
|
+
car,
|
|
573
|
+
track,
|
|
574
|
+
centerState: providedCenterState = null,
|
|
575
|
+
geometry: providedGeometry = null,
|
|
576
|
+
scratch = null,
|
|
577
|
+
allowPitOverride: providedAllowPitOverride = null,
|
|
578
|
+
trackStateTarget = null,
|
|
579
|
+
}) {
|
|
580
|
+
const geometry = providedGeometry ?? getCurrentVehicleGeometryState(car);
|
|
58
581
|
const trackLimit = track.width / 2;
|
|
59
|
-
const allowPitOverride = pitOverrideAllowedForCar(car);
|
|
582
|
+
const allowPitOverride = providedAllowPitOverride ?? pitOverrideAllowedForCar(car);
|
|
60
583
|
const centerState = providedCenterState == null
|
|
61
|
-
?
|
|
584
|
+
? canRefreshAutomaticCenterStateFromTrackState(car, track)
|
|
585
|
+
? car.trackState
|
|
586
|
+
: nearestTrackStateForCar(track, car, car, car.progress, { allowPitOverride })
|
|
62
587
|
: !allowPitOverride && providedCenterState.inPitLane
|
|
63
588
|
? nearestTrackStateForCar(track, car, car, car.progress, { allowPitOverride: false })
|
|
64
589
|
: providedCenterState;
|
|
590
|
+
const nearPitConnector = !centerState.inPitLane && isNearPitConnector(track, centerState);
|
|
591
|
+
const scratchWheels = prepareScratchWheels(scratch, geometry.contactPatches.length);
|
|
592
|
+
const scratchSummary = prepareWheelSummary(scratch);
|
|
593
|
+
|
|
594
|
+
if (!centerState.inPitLane && !nearPitConnector) {
|
|
595
|
+
const wheels = scratchWheels ?? createWheelTargets(geometry.contactPatches.length);
|
|
596
|
+
if (!scratchWheels) recordScratchlessScalarWheelBatch(car, geometry.contactPatches.length);
|
|
597
|
+
const summary = writeAnalyticWheelStatesWithSummary(
|
|
598
|
+
wheels,
|
|
599
|
+
geometry.contactPatches,
|
|
600
|
+
centerState,
|
|
601
|
+
track,
|
|
602
|
+
trackLimit,
|
|
603
|
+
scratchSummary,
|
|
604
|
+
);
|
|
605
|
+
return {
|
|
606
|
+
wheels,
|
|
607
|
+
effectiveSurface: summary.effectiveSurface,
|
|
608
|
+
trackLimits: summary.trackLimits,
|
|
609
|
+
sampleMode: 'analytic',
|
|
610
|
+
representativeState: writeRepresentativeTrackState(
|
|
611
|
+
trackStateTarget ?? {},
|
|
612
|
+
centerState,
|
|
613
|
+
summary.effectiveSurface,
|
|
614
|
+
summary.representative,
|
|
615
|
+
summary.allOnTrackOrPit,
|
|
616
|
+
false,
|
|
617
|
+
),
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
|
|
65
621
|
const useAnalyticPitSampling = canUseAnalyticPitWheels(geometry, centerState);
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
622
|
+
const useConnectorSampling = !useAnalyticPitSampling && nearPitConnector;
|
|
623
|
+
const connectorPitChecksSkipped = useConnectorSampling && canSkipConnectorPitChecks(track, geometry, centerState);
|
|
624
|
+
const connectorStraightMainTrackAnalytic = useConnectorSampling &&
|
|
625
|
+
!connectorPitChecksSkipped &&
|
|
626
|
+
canUseStraightConnectorMainTrackAnalytic(track, geometry, centerState, trackLimit);
|
|
627
|
+
const useFullSampling = !useAnalyticPitSampling && !useConnectorSampling && centerState.inPitLane;
|
|
628
|
+
const connectorProgressHint = centerState.distance ?? car.progress ?? null;
|
|
629
|
+
let analyticSummary = null;
|
|
630
|
+
const wheels = scratchWheels ?? createWheelTargets(geometry.contactPatches.length);
|
|
631
|
+
if (scratchWheels) {
|
|
632
|
+
if (useAnalyticPitSampling) {
|
|
633
|
+
analyticSummary = writeAnalyticPitWheelStatesWithSummary(
|
|
634
|
+
scratchWheels,
|
|
635
|
+
geometry.contactPatches,
|
|
636
|
+
centerState,
|
|
637
|
+
track,
|
|
638
|
+
trackLimit,
|
|
639
|
+
scratchSummary,
|
|
640
|
+
);
|
|
641
|
+
} else if (connectorPitChecksSkipped || connectorStraightMainTrackAnalytic) {
|
|
642
|
+
analyticSummary = writeAnalyticWheelStatesWithSummary(
|
|
643
|
+
scratchWheels,
|
|
644
|
+
geometry.contactPatches,
|
|
645
|
+
centerState,
|
|
646
|
+
track,
|
|
647
|
+
trackLimit,
|
|
648
|
+
scratchSummary,
|
|
649
|
+
);
|
|
650
|
+
} else if (useConnectorSampling) {
|
|
651
|
+
const sampledStatePools = scratch.sampledStatePools ?? [];
|
|
652
|
+
const connectorProjectionPools = scratch.connectorProjectionPools ?? [];
|
|
653
|
+
const connectorPositionPools = scratch.connectorPositionPools ?? [];
|
|
654
|
+
scratch.sampledStatePools = sampledStatePools;
|
|
655
|
+
scratch.connectorProjectionPools = connectorProjectionPools;
|
|
656
|
+
scratch.connectorPositionPools = connectorPositionPools;
|
|
657
|
+
const connectorQueriedStatePools = scratch.connectorQueriedStatePools ?? [];
|
|
658
|
+
scratch.connectorQueriedStatePools = connectorQueriedStatePools;
|
|
659
|
+
const wheelCenterProjections = connectorWheelCenterProjections(
|
|
660
|
+
track,
|
|
661
|
+
car,
|
|
662
|
+
geometry.contactPatches,
|
|
663
|
+
centerState,
|
|
664
|
+
connectorProgressHint,
|
|
665
|
+
connectorProjectionPools,
|
|
666
|
+
connectorPositionPools,
|
|
667
|
+
);
|
|
668
|
+
const mainRoadEdge = track.width / 2 + (track.kerbWidth ?? 0);
|
|
669
|
+
let allWheelCentersOnMainTrack = Array.isArray(wheelCenterProjections);
|
|
670
|
+
for (let index = 0; allWheelCentersOnMainTrack && index < wheelCenterProjections.length; index += 1) {
|
|
671
|
+
const projection = wheelCenterProjections[index];
|
|
672
|
+
if (!projection || projection.crossTrackError > mainRoadEdge) allWheelCentersOnMainTrack = false;
|
|
673
|
+
}
|
|
674
|
+
if (allWheelCentersOnMainTrack) {
|
|
675
|
+
connectorQueriedStatePools.length = 0;
|
|
676
|
+
analyticSummary = writeAnalyticWheelStatesFromTrackStatesWithSummary(
|
|
677
|
+
scratchWheels,
|
|
678
|
+
geometry.contactPatches,
|
|
679
|
+
wheelCenterProjections,
|
|
680
|
+
track,
|
|
681
|
+
trackLimit,
|
|
682
|
+
scratchSummary,
|
|
683
|
+
);
|
|
684
|
+
} else {
|
|
685
|
+
const wheelCenterStates = wheelCenterProjections
|
|
686
|
+
? writeLocalSegmentTrackStatesFromProjections(
|
|
687
|
+
track,
|
|
688
|
+
connectorPositionPools,
|
|
689
|
+
connectorProgressHint,
|
|
690
|
+
wheelCenterProjections,
|
|
691
|
+
{
|
|
692
|
+
allowPitOverride,
|
|
693
|
+
skipPitOverrideInsideMainRoad: true,
|
|
694
|
+
target: connectorQueriedStatePools,
|
|
695
|
+
},
|
|
696
|
+
)
|
|
697
|
+
: connectorWheelCenterStates(
|
|
698
|
+
track,
|
|
699
|
+
car,
|
|
700
|
+
geometry.contactPatches,
|
|
701
|
+
centerState,
|
|
702
|
+
connectorProgressHint,
|
|
703
|
+
connectorQueriedStatePools,
|
|
704
|
+
connectorProjectionPools,
|
|
705
|
+
connectorPositionPools,
|
|
706
|
+
);
|
|
707
|
+
analyticSummary = writeConnectorWheelStatesWithSummary({
|
|
708
|
+
targetWheels: scratchWheels,
|
|
709
|
+
patches: geometry.contactPatches,
|
|
710
|
+
wheelCenterStates,
|
|
711
|
+
centerState,
|
|
712
|
+
car,
|
|
713
|
+
track,
|
|
714
|
+
trackLimit,
|
|
715
|
+
progressHint: connectorProgressHint,
|
|
716
|
+
sampledStatePools,
|
|
717
|
+
summaryTarget: scratchSummary,
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
} else if (useFullSampling) {
|
|
721
|
+
const sampledStatePools = scratch.sampledStatePools ?? [];
|
|
722
|
+
scratch.sampledStatePools = sampledStatePools;
|
|
723
|
+
analyticSummary = writeNearestSampledWheelStatesWithSummary({
|
|
724
|
+
targetWheels: scratchWheels,
|
|
725
|
+
patches: geometry.contactPatches,
|
|
726
|
+
car,
|
|
727
|
+
track,
|
|
728
|
+
trackLimit,
|
|
729
|
+
sampledStatePools,
|
|
730
|
+
summaryTarget: scratchSummary,
|
|
731
|
+
});
|
|
732
|
+
} else {
|
|
733
|
+
analyticSummary = writeAnalyticWheelStatesWithSummary(
|
|
734
|
+
scratchWheels,
|
|
735
|
+
geometry.contactPatches,
|
|
736
|
+
centerState,
|
|
737
|
+
track,
|
|
738
|
+
trackLimit,
|
|
739
|
+
scratchSummary,
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
} else if (useAnalyticPitSampling) {
|
|
743
|
+
recordScratchlessScalarWheelBatch(car, geometry.contactPatches.length);
|
|
744
|
+
analyticSummary = writeAnalyticPitWheelStatesWithSummary(
|
|
745
|
+
wheels,
|
|
746
|
+
geometry.contactPatches,
|
|
747
|
+
centerState,
|
|
748
|
+
track,
|
|
749
|
+
trackLimit,
|
|
750
|
+
);
|
|
751
|
+
} else if (connectorPitChecksSkipped || connectorStraightMainTrackAnalytic) {
|
|
752
|
+
recordScratchlessScalarWheelBatch(car, geometry.contactPatches.length);
|
|
753
|
+
analyticSummary = writeAnalyticWheelStatesWithSummary(
|
|
754
|
+
wheels,
|
|
755
|
+
geometry.contactPatches,
|
|
756
|
+
centerState,
|
|
757
|
+
track,
|
|
758
|
+
trackLimit,
|
|
759
|
+
);
|
|
760
|
+
} else if (useConnectorSampling) {
|
|
761
|
+
recordScratchlessScalarWheelBatch(car, geometry.contactPatches.length);
|
|
762
|
+
const wheelCenterStates = connectorWheelCenterStates(track, car, geometry.contactPatches, centerState, connectorProgressHint);
|
|
763
|
+
analyticSummary = writeConnectorWheelStatesWithSummary({
|
|
764
|
+
targetWheels: wheels,
|
|
765
|
+
patches: geometry.contactPatches,
|
|
766
|
+
wheelCenterStates,
|
|
767
|
+
centerState,
|
|
768
|
+
car,
|
|
769
|
+
track,
|
|
770
|
+
trackLimit,
|
|
771
|
+
progressHint: connectorProgressHint,
|
|
772
|
+
sampledStatePools: new Array(geometry.contactPatches.length),
|
|
773
|
+
});
|
|
774
|
+
} else if (useFullSampling) {
|
|
775
|
+
recordScratchlessScalarWheelBatch(car, geometry.contactPatches.length);
|
|
776
|
+
analyticSummary = writeNearestSampledWheelStatesWithSummary({
|
|
777
|
+
targetWheels: wheels,
|
|
778
|
+
patches: geometry.contactPatches,
|
|
779
|
+
car,
|
|
780
|
+
track,
|
|
781
|
+
trackLimit,
|
|
782
|
+
sampledStatePools: new Array(geometry.contactPatches.length),
|
|
783
|
+
});
|
|
784
|
+
} else {
|
|
785
|
+
recordScratchlessScalarWheelBatch(car, geometry.contactPatches.length);
|
|
786
|
+
analyticSummary = writeAnalyticWheelStatesWithSummary(
|
|
787
|
+
wheels,
|
|
788
|
+
geometry.contactPatches,
|
|
789
|
+
centerState,
|
|
790
|
+
track,
|
|
791
|
+
trackLimit,
|
|
792
|
+
);
|
|
793
|
+
}
|
|
794
|
+
const summary = analyticSummary;
|
|
795
|
+
const effectiveSurface = summary.effectiveSurface;
|
|
796
|
+
const representative = summary.representative;
|
|
797
|
+
const trackLimits = summary.trackLimits;
|
|
101
798
|
|
|
102
799
|
return {
|
|
103
800
|
wheels,
|
|
104
801
|
effectiveSurface,
|
|
105
802
|
trackLimits,
|
|
106
|
-
sampleMode: useAnalyticPitSampling
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
803
|
+
sampleMode: useAnalyticPitSampling
|
|
804
|
+
? 'pit-analytic'
|
|
805
|
+
: useConnectorSampling
|
|
806
|
+
? 'connector-analytic'
|
|
807
|
+
: useFullSampling
|
|
808
|
+
? 'full'
|
|
809
|
+
: 'analytic',
|
|
810
|
+
representativeState: representative
|
|
811
|
+
? writeRepresentativeTrackState(
|
|
812
|
+
trackStateTarget ?? {},
|
|
813
|
+
centerState,
|
|
814
|
+
effectiveSurface,
|
|
815
|
+
representative,
|
|
816
|
+
summary.allOnTrackOrPit,
|
|
817
|
+
summary.allInPitLane,
|
|
818
|
+
)
|
|
819
|
+
: nearestTrackStateForCar(track, car),
|
|
115
820
|
};
|
|
116
821
|
}
|
|
117
822
|
|
|
118
823
|
export function applyWheelSurfaceState(car, track, options = {}) {
|
|
119
|
-
const geometry =
|
|
120
|
-
const
|
|
121
|
-
track,
|
|
122
|
-
geometrySignature: geometry.signature,
|
|
123
|
-
centerSignature: centerStateSignature(options.centerState),
|
|
124
|
-
pitOverrideAllowed: pitOverrideAllowedForCar(car),
|
|
125
|
-
};
|
|
824
|
+
const geometry = getCurrentVehicleGeometryState(car);
|
|
825
|
+
const allowPitOverride = options.allowPitOverride ?? pitOverrideAllowedForCar(car);
|
|
126
826
|
const cached = car.wheelSurfaceCache;
|
|
127
827
|
if (
|
|
128
|
-
cached?.track ===
|
|
129
|
-
cached.
|
|
130
|
-
cached.
|
|
131
|
-
cached.
|
|
828
|
+
cached?.track === track &&
|
|
829
|
+
cached.geometryX === (geometry.pose?.x ?? null) &&
|
|
830
|
+
cached.geometryY === (geometry.pose?.y ?? null) &&
|
|
831
|
+
cached.geometryHeading === (geometry.pose?.heading ?? null) &&
|
|
832
|
+
cacheMatchesCenterState(cached, options.centerState, options.cacheAsAuto) &&
|
|
833
|
+
cached.pitOverrideAllowed === allowPitOverride
|
|
132
834
|
) {
|
|
133
835
|
car.wheelStates = cached.result.wheels;
|
|
134
836
|
car.trackLimitState = cached.result.trackLimits;
|
|
@@ -136,17 +838,30 @@ export function applyWheelSurfaceState(car, track, options = {}) {
|
|
|
136
838
|
return cached.result;
|
|
137
839
|
}
|
|
138
840
|
|
|
139
|
-
const result = calculateWheelSurfaceState({
|
|
841
|
+
const result = calculateWheelSurfaceState({
|
|
842
|
+
car,
|
|
843
|
+
track,
|
|
844
|
+
centerState: options.centerState,
|
|
845
|
+
geometry,
|
|
846
|
+
scratch: car.wheelSurfaceScratch ??= {},
|
|
847
|
+
allowPitOverride,
|
|
848
|
+
trackStateTarget: car.trackState ?? {},
|
|
849
|
+
});
|
|
140
850
|
car.wheelStates = result.wheels;
|
|
141
851
|
car.trackLimitState = result.trackLimits;
|
|
142
|
-
car.trackState =
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
852
|
+
car.trackState = result.representativeState;
|
|
853
|
+
car.trackStatePoseX = car.x;
|
|
854
|
+
car.trackStatePoseY = car.y;
|
|
855
|
+
car.trackStatePoseHeading = car.heading;
|
|
856
|
+
const cache = cached ?? {};
|
|
857
|
+
cache.track = track;
|
|
858
|
+
cache.geometryX = geometry.pose?.x ?? null;
|
|
859
|
+
cache.geometryY = geometry.pose?.y ?? null;
|
|
860
|
+
cache.geometryHeading = geometry.pose?.heading ?? null;
|
|
861
|
+
writeCachedCenterState(cache, options.centerState, options.cacheAsAuto);
|
|
862
|
+
cache.pitOverrideAllowed = allowPitOverride;
|
|
863
|
+
cache.result = result;
|
|
864
|
+
cache.trackState = car.trackState;
|
|
865
|
+
car.wheelSurfaceCache = cache;
|
|
151
866
|
return result;
|
|
152
867
|
}
|