@inventure71/paddockjs 4.2.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 +34 -0
- package/INSTALL_AND_UPDATE.md +23 -4
- package/README.md +31 -2
- package/docs/advanced_ai_pace.md +131 -0
- package/docs/advanced_physics_rebuild.md +166 -0
- package/docs/architecture.md +109 -30
- package/docs/cleanup_coverage.md +284 -0
- package/docs/component_inventory.md +31 -15
- 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/grid_walk.md +80 -0
- package/docs/index.md +1 -0
- package/docs/keyboard_driving_fix.md +118 -0
- package/docs/limit_driving_2026-10-14.md +88 -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 +36 -11
- package/docs/testing-reliability-guide.md +5 -0
- package/docs/v11_improvements.md +108 -0
- package/package.json +23 -13
- 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 +13 -259
- 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 +21 -140
- 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 +34 -259
- 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 +5 -11
- 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 +14 -3
- package/src/simulation/driver/edgeRecovery.js +0 -1
- package/src/simulation/driver/racingControls.js +10 -406
- package/src/simulation/driver/racingDriverProfile.js +48 -0
- package/src/simulation/driver/racingLinePlan.js +48 -15
- package/src/simulation/driver/rejoinControls.js +58 -111
- package/src/simulation/driver/safetyCarControls.js +12 -0
- package/src/simulation/driver/trafficScan.js +89 -6
- 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 +1 -1
- package/src/simulation/race/redFlag.js +2 -4
- package/src/simulation/race/safetyCar.js +3 -8
- 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/rulesReview.js +1 -7
- 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 +3 -3
- 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 +1 -25
- 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 +4 -241
- 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 +20 -6
- 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 +0 -56
- package/src/simulation/vehicle/vehicleKinematics.js +10 -0
- package/src/simulation/vehicle/vehiclePhysics.js +35 -194
- package/src/simulation/vehicle/vehicleSnapshots.js +13 -4
- package/src/simulation/vehicle/vehicleState.js +2 -1
- 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/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
|
@@ -1,561 +1,7 @@
|
|
|
1
|
-
import { createCarRayMiss, estimateCarHit } from './carRays.js';
|
|
2
|
-
import { normalizeRayOptions } from './rayConfig.js';
|
|
3
|
-
import { degreesToRadians, getCarRayOrigin } from './rayGeometry.js';
|
|
4
|
-
import { isSelfCarTarget, rayDetectableTargetsForSnapshot } from './sensorTargets.js';
|
|
5
|
-
import { createTrackMiss, createTrackRayContext, estimateTrackHit } from './trackRays.js';
|
|
6
|
-
import { createSurfaceMiss, estimateSurfaceHits } from './surfaceRays.js';
|
|
7
|
-
import { canUseBatchTrainingRayApproximation } from './rayGuards.js';
|
|
8
|
-
import { traceIndexedRayBands } from './rayBandTrace.js';
|
|
9
|
-
import { createRayChannelFlags, writeRayChannelFlags } from './rayChannels.js';
|
|
10
|
-
import { pitOverrideAllowedForCar } from '../../simulation/track/trackStatePolicy.js';
|
|
11
|
-
|
|
12
1
|
export { buildNearbyCars } from './nearbyCars.js';
|
|
13
2
|
export { DEFAULT_RAY_ANGLES_DEGREES } from './rayDefaults.js';
|
|
14
3
|
export { RAY_CHANNELS } from './rayChannels.js';
|
|
15
4
|
export { normalizeRayOptions, RAY_LAYOUT_PRESETS } from './rayConfig.js';
|
|
16
5
|
export { getCarRayOrigin, getCarRayVector } from './rayGeometry.js';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export function createRayBatchContext(snapshot, { scratch = false } = {}) {
|
|
21
|
-
const contextScratch = scratch === true
|
|
22
|
-
? {}
|
|
23
|
-
: scratch && typeof scratch === 'object'
|
|
24
|
-
? scratch
|
|
25
|
-
: null;
|
|
26
|
-
const context = {
|
|
27
|
-
rayTargets: rayDetectableTargetsForSnapshot(snapshot, contextScratch),
|
|
28
|
-
};
|
|
29
|
-
if (contextScratch) context.scratch = contextScratch;
|
|
30
|
-
return context;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function getRayScratch(batchContext) {
|
|
34
|
-
return batchContext?.scratch ?? null;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function getNormalizedRayOptions(rayOptions, scratch) {
|
|
38
|
-
if (!scratch || !rayOptions || typeof rayOptions !== 'object') return normalizeRayOptions(rayOptions);
|
|
39
|
-
if (rayOptions?.rays && rayOptions?.channels && rayOptions?.anglesDegrees) {
|
|
40
|
-
return normalizeRayOptions(rayOptions);
|
|
41
|
-
}
|
|
42
|
-
const cache = scratch.normalizedRayOptionsBySource ?? new WeakMap();
|
|
43
|
-
scratch.normalizedRayOptionsBySource = cache;
|
|
44
|
-
const cached = cache.get(rayOptions);
|
|
45
|
-
if (cached) return cached;
|
|
46
|
-
const normalized = normalizeRayOptions(rayOptions);
|
|
47
|
-
cache.set(rayOptions, normalized);
|
|
48
|
-
return normalized;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function getRayOrigin(car, scratch) {
|
|
52
|
-
if (!scratch) return getCarRayOrigin(car);
|
|
53
|
-
const origin = scratch.origin ?? {};
|
|
54
|
-
scratch.origin = origin;
|
|
55
|
-
origin.x = car.x;
|
|
56
|
-
origin.y = car.y;
|
|
57
|
-
return origin;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function getFilteredCarTargets(car, snapshot, batchContext, scratch) {
|
|
61
|
-
const targets = batchContext?.rayTargets ?? rayDetectableTargetsForSnapshot(snapshot);
|
|
62
|
-
if (!scratch) return targets.filter((target) => !isSelfCarTarget(car, target));
|
|
63
|
-
const filtered = scratch.carTargets ?? [];
|
|
64
|
-
scratch.carTargets = filtered;
|
|
65
|
-
let count = 0;
|
|
66
|
-
for (let index = 0; index < targets.length; index += 1) {
|
|
67
|
-
const target = targets[index];
|
|
68
|
-
if (isSelfCarTarget(car, target)) continue;
|
|
69
|
-
filtered[count] = target;
|
|
70
|
-
count += 1;
|
|
71
|
-
}
|
|
72
|
-
filtered.length = count;
|
|
73
|
-
return filtered;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function prepareScratchArray(scratch, arrayKey, poolKey, count, createItem) {
|
|
77
|
-
if (!scratch) return null;
|
|
78
|
-
const values = scratch[arrayKey] ?? [];
|
|
79
|
-
const pool = scratch[poolKey] ?? [];
|
|
80
|
-
scratch[arrayKey] = values;
|
|
81
|
-
scratch[poolKey] = pool;
|
|
82
|
-
values.length = count;
|
|
83
|
-
for (let index = 0; index < count; index += 1) {
|
|
84
|
-
const item = pool[index] ?? createItem();
|
|
85
|
-
pool[index] = item;
|
|
86
|
-
values[index] = item;
|
|
87
|
-
}
|
|
88
|
-
return values;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function getScratchRayVector(scratch, index, car, angleRadians) {
|
|
92
|
-
if (!scratch) {
|
|
93
|
-
return {
|
|
94
|
-
x: Math.cos((car.heading ?? 0) + angleRadians),
|
|
95
|
-
y: Math.sin((car.heading ?? 0) + angleRadians),
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
const vectors = scratch.rayVectors ?? [];
|
|
99
|
-
scratch.rayVectors = vectors;
|
|
100
|
-
const vector = vectors[index] ?? {};
|
|
101
|
-
vectors[index] = vector;
|
|
102
|
-
vector.x = Math.cos((car.heading ?? 0) + angleRadians);
|
|
103
|
-
vector.y = Math.sin((car.heading ?? 0) + angleRadians);
|
|
104
|
-
return vector;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function getSharedRayQuery(scratch, index) {
|
|
108
|
-
if (!scratch) return {};
|
|
109
|
-
const queries = scratch.sharedRayQueries ?? [];
|
|
110
|
-
scratch.sharedRayQueries = queries;
|
|
111
|
-
const query = queries[index] ?? {};
|
|
112
|
-
queries[index] = query;
|
|
113
|
-
query.carHitResult = query.carHitResult ?? {};
|
|
114
|
-
query.surfaceBoundaries = null;
|
|
115
|
-
query.trackBandBoundaries = null;
|
|
116
|
-
query.boundaryStateCache = null;
|
|
117
|
-
query.sampleStateCache = null;
|
|
118
|
-
return query;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function getCarRayStats(scratch) {
|
|
122
|
-
if (!scratch) return null;
|
|
123
|
-
const stats = scratch.carRayStats ?? {
|
|
124
|
-
callerVectorCount: 0,
|
|
125
|
-
computedVectorCount: 0,
|
|
126
|
-
resultTargetCount: 0,
|
|
127
|
-
};
|
|
128
|
-
scratch.carRayStats = stats;
|
|
129
|
-
return stats;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function estimateCarHitForRay(car, snapshot, ray, origin, vector, carTargets, sharedRayQuery, scratch) {
|
|
133
|
-
return estimateCarHit(car, snapshot, ray.angleDegrees, ray.lengthMeters, origin, carTargets, {
|
|
134
|
-
rayVector: vector,
|
|
135
|
-
resultTarget: sharedRayQuery?.carHitResult,
|
|
136
|
-
stats: getCarRayStats(scratch),
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function getRayBandTraceStats(scratch) {
|
|
141
|
-
if (!scratch) return null;
|
|
142
|
-
const stats = scratch.rayBandTraceStats ?? {
|
|
143
|
-
directPathCount: 0,
|
|
144
|
-
sampledPathCount: 0,
|
|
145
|
-
fallbackCount: 0,
|
|
146
|
-
channelSetAllocations: 0,
|
|
147
|
-
};
|
|
148
|
-
scratch.rayBandTraceStats = stats;
|
|
149
|
-
return stats;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function createRayChannelContext(normalized) {
|
|
153
|
-
if (normalized?.[CHANNEL_CONTEXT]) return normalized[CHANNEL_CONTEXT];
|
|
154
|
-
const channels = writeRayChannelFlags(createRayChannelFlags(), normalized.channels);
|
|
155
|
-
const context = {
|
|
156
|
-
channels,
|
|
157
|
-
surfaceChannels: channels.surfaceChannels,
|
|
158
|
-
hasRoadEdge: channels.hasRoadEdge,
|
|
159
|
-
hasCar: channels.hasCar,
|
|
160
|
-
hasRoadOrSurface: channels.hasRoadEdge || channels.surfaceChannels.length > 0,
|
|
161
|
-
};
|
|
162
|
-
if (normalized) {
|
|
163
|
-
Object.defineProperty(normalized, CHANNEL_CONTEXT, {
|
|
164
|
-
value: context,
|
|
165
|
-
enumerable: false,
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
return context;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function estimateRayBands({
|
|
172
|
-
car,
|
|
173
|
-
snapshot,
|
|
174
|
-
ray,
|
|
175
|
-
angleDegrees,
|
|
176
|
-
origin,
|
|
177
|
-
vector,
|
|
178
|
-
normalized,
|
|
179
|
-
channelContext,
|
|
180
|
-
trackContext,
|
|
181
|
-
sharedRayQuery,
|
|
182
|
-
scratch,
|
|
183
|
-
}) {
|
|
184
|
-
const stats = getRayBandTraceStats(scratch);
|
|
185
|
-
const canUseRayBandTrace = channelContext.hasRoadOrSurface &&
|
|
186
|
-
trackContext?.originState;
|
|
187
|
-
if (canUseRayBandTrace) {
|
|
188
|
-
const direct = traceIndexedRayBands({
|
|
189
|
-
track: snapshot.track,
|
|
190
|
-
origin,
|
|
191
|
-
vector,
|
|
192
|
-
lengthMeters: ray.lengthMeters,
|
|
193
|
-
originState: trackContext.originState,
|
|
194
|
-
channels: channelContext.channels,
|
|
195
|
-
precision: normalized.precision,
|
|
196
|
-
sharedRayQuery,
|
|
197
|
-
stats,
|
|
198
|
-
validateBoundaries: car?.interaction?.profile !== 'batch-training',
|
|
199
|
-
allowPitOverride: car?.interaction?.profile === 'batch-training'
|
|
200
|
-
? false
|
|
201
|
-
: pitOverrideAllowedForCar(car),
|
|
202
|
-
mainTrackOnly: car?.interaction?.profile === 'batch-training',
|
|
203
|
-
});
|
|
204
|
-
if (direct.available) {
|
|
205
|
-
if (stats) {
|
|
206
|
-
if (direct.path === 'sampled') stats.sampledPathCount += 1;
|
|
207
|
-
else stats.directPathCount += 1;
|
|
208
|
-
}
|
|
209
|
-
return {
|
|
210
|
-
roadEdge: direct.roadEdge,
|
|
211
|
-
surfaceHits: {
|
|
212
|
-
kerb: direct.kerb,
|
|
213
|
-
illegalSurface: direct.illegalSurface,
|
|
214
|
-
},
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
if (stats) stats.fallbackCount += 1;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return {
|
|
221
|
-
roadEdge: channelContext.hasRoadEdge
|
|
222
|
-
? estimateTrackHit(
|
|
223
|
-
car,
|
|
224
|
-
snapshot,
|
|
225
|
-
angleDegrees,
|
|
226
|
-
ray.lengthMeters,
|
|
227
|
-
trackContext,
|
|
228
|
-
{ precision: normalized.precision, sharedRayQuery },
|
|
229
|
-
)
|
|
230
|
-
: createTrackMiss(ray.lengthMeters),
|
|
231
|
-
surfaceHits: estimateSurfaceHits(
|
|
232
|
-
car,
|
|
233
|
-
snapshot,
|
|
234
|
-
ray,
|
|
235
|
-
origin,
|
|
236
|
-
vector,
|
|
237
|
-
channelContext.surfaceChannels,
|
|
238
|
-
trackContext,
|
|
239
|
-
{ precision: normalized.precision, sharedRayQuery },
|
|
240
|
-
),
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
function writeRichRay(target, ray, angleRadians, roadEdge, carHit, surfaceHits, channels) {
|
|
245
|
-
target.id = ray.id;
|
|
246
|
-
target.angleDegrees = ray.angleDegrees;
|
|
247
|
-
target.angleRadians = angleRadians;
|
|
248
|
-
target.lengthMeters = ray.lengthMeters;
|
|
249
|
-
target.roadEdge = writeTrackRayHit(target.roadEdge ?? {}, roadEdge);
|
|
250
|
-
target.track = target.roadEdge;
|
|
251
|
-
target.kerb = channels.hasKerb
|
|
252
|
-
? writeSurfaceRayHit(target.kerb ?? {}, surfaceHits.kerb ?? createSurfaceMiss(ray.lengthMeters))
|
|
253
|
-
: writeSurfaceRayHit(target.kerb ?? {}, createSurfaceMiss(ray.lengthMeters));
|
|
254
|
-
target.illegalSurface = channels.hasIllegalSurface
|
|
255
|
-
? writeSurfaceRayHit(target.illegalSurface ?? {}, surfaceHits.illegalSurface ?? createSurfaceMiss(ray.lengthMeters))
|
|
256
|
-
: writeSurfaceRayHit(target.illegalSurface ?? {}, createSurfaceMiss(ray.lengthMeters));
|
|
257
|
-
target.car = writeCarRayHit(target.car ?? {}, carHit);
|
|
258
|
-
return target;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
function writeTrackRayHit(target, source) {
|
|
262
|
-
target.hit = Boolean(source?.hit);
|
|
263
|
-
target.distanceMeters = source?.distanceMeters;
|
|
264
|
-
target.kind = source?.kind ?? null;
|
|
265
|
-
return target;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function writeSurfaceRayHit(target, source) {
|
|
269
|
-
target.hit = Boolean(source?.hit);
|
|
270
|
-
target.distanceMeters = source?.distanceMeters;
|
|
271
|
-
target.surface = source?.surface ?? null;
|
|
272
|
-
return target;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function writeCarRayHit(target, source) {
|
|
276
|
-
target.hit = Boolean(source?.hit);
|
|
277
|
-
target.distanceMeters = source?.distanceMeters;
|
|
278
|
-
target.driverId = source?.driverId ?? null;
|
|
279
|
-
target.targetId = source?.targetId ?? null;
|
|
280
|
-
target.targetType = source?.targetType ?? null;
|
|
281
|
-
target.relativeSpeedKph = source?.relativeSpeedKph ?? 0;
|
|
282
|
-
return target;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
export function buildRaySensors(car, snapshot, rayOptions = {}, batchContext = null) {
|
|
286
|
-
const scratch = getRayScratch(batchContext);
|
|
287
|
-
const normalized = getNormalizedRayOptions(rayOptions, scratch);
|
|
288
|
-
if (!normalized.enabled) return [];
|
|
289
|
-
const channelContext = createRayChannelContext(normalized);
|
|
290
|
-
if (car?.destroyed || car?.outOfRace) {
|
|
291
|
-
return buildInactiveCarRays(normalized, scratch);
|
|
292
|
-
}
|
|
293
|
-
const origin = getRayOrigin(car, scratch);
|
|
294
|
-
const usesTrackContext = channelContext.hasRoadOrSurface;
|
|
295
|
-
const trackContext = !usesTrackContext
|
|
296
|
-
? null
|
|
297
|
-
: createTrackRayContext(car, snapshot, origin, normalized.precision);
|
|
298
|
-
const carTargets = channelContext.hasCar
|
|
299
|
-
? getFilteredCarTargets(car, snapshot, batchContext, scratch)
|
|
300
|
-
: [];
|
|
301
|
-
|
|
302
|
-
if (canUseFastBatchTrainingRays(car, snapshot, trackContext)) {
|
|
303
|
-
return buildFastBatchTrainingRays(car, snapshot, normalized, channelContext, origin, carTargets, trackContext, scratch);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const scratchRays = prepareScratchArray(scratch, 'rays', 'rayPool', normalized.rays.length, () => ({}));
|
|
307
|
-
const rays = scratchRays ?? normalized.rays.map(() => ({}));
|
|
308
|
-
for (let index = 0; index < normalized.rays.length; index += 1) {
|
|
309
|
-
const ray = normalized.rays[index];
|
|
310
|
-
const angleDegrees = ray.angleDegrees;
|
|
311
|
-
const angleRadians = degreesToRadians(angleDegrees);
|
|
312
|
-
const vector = getScratchRayVector(scratch, index, car, angleRadians);
|
|
313
|
-
const sharedRayQuery = getSharedRayQuery(scratch, index);
|
|
314
|
-
const { roadEdge, surfaceHits } = estimateRayBands({
|
|
315
|
-
car,
|
|
316
|
-
snapshot,
|
|
317
|
-
ray,
|
|
318
|
-
angleDegrees,
|
|
319
|
-
origin,
|
|
320
|
-
vector,
|
|
321
|
-
normalized,
|
|
322
|
-
channelContext,
|
|
323
|
-
trackContext,
|
|
324
|
-
sharedRayQuery,
|
|
325
|
-
scratch,
|
|
326
|
-
});
|
|
327
|
-
const carHit = channelContext.hasCar
|
|
328
|
-
? estimateCarHitForRay(car, snapshot, ray, origin, vector, carTargets, sharedRayQuery, scratch)
|
|
329
|
-
: createCarRayMiss(ray.lengthMeters);
|
|
330
|
-
|
|
331
|
-
writeRichRay(rays[index], ray, angleRadians, roadEdge, carHit, surfaceHits, channelContext.channels);
|
|
332
|
-
}
|
|
333
|
-
return rays;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
export function buildRaySensorVectorValues(car, snapshot, rayOptions = {}, batchContext = null) {
|
|
337
|
-
const scratch = getRayScratch(batchContext);
|
|
338
|
-
const normalized = getNormalizedRayOptions(rayOptions, scratch);
|
|
339
|
-
if (!normalized.enabled) return [];
|
|
340
|
-
const channelContext = createRayChannelContext(normalized);
|
|
341
|
-
const surfaceChannels = channelContext.surfaceChannels;
|
|
342
|
-
if (car?.destroyed || car?.outOfRace) {
|
|
343
|
-
return buildInactiveRayVectorValues(normalized, surfaceChannels, scratch);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
const origin = getRayOrigin(car, scratch);
|
|
347
|
-
const usesTrackContext = channelContext.hasRoadOrSurface;
|
|
348
|
-
const trackContext = !usesTrackContext
|
|
349
|
-
? null
|
|
350
|
-
: createTrackRayContext(car, snapshot, origin, normalized.precision);
|
|
351
|
-
const carTargets = channelContext.hasCar
|
|
352
|
-
? getFilteredCarTargets(car, snapshot, batchContext, scratch)
|
|
353
|
-
: [];
|
|
354
|
-
|
|
355
|
-
const vectorValues = prepareScratchArray(scratch, 'rayVectorValues', 'rayVectorValuePool', normalized.rays.length, () => []);
|
|
356
|
-
const values = vectorValues ?? normalized.rays.map(() => []);
|
|
357
|
-
for (let index = 0; index < normalized.rays.length; index += 1) {
|
|
358
|
-
const ray = normalized.rays[index];
|
|
359
|
-
const angleRadians = degreesToRadians(ray.angleDegrees);
|
|
360
|
-
const vector = getScratchRayVector(scratch, index, car, angleRadians);
|
|
361
|
-
const sharedRayQuery = getSharedRayQuery(scratch, index);
|
|
362
|
-
const { roadEdge, surfaceHits } = estimateRayBands({
|
|
363
|
-
car,
|
|
364
|
-
snapshot,
|
|
365
|
-
ray,
|
|
366
|
-
angleDegrees: ray.angleDegrees,
|
|
367
|
-
origin,
|
|
368
|
-
vector,
|
|
369
|
-
normalized,
|
|
370
|
-
channelContext,
|
|
371
|
-
trackContext,
|
|
372
|
-
sharedRayQuery,
|
|
373
|
-
scratch,
|
|
374
|
-
});
|
|
375
|
-
const carHit = channelContext.hasCar
|
|
376
|
-
? estimateCarHitForRay(car, snapshot, ray, origin, vector, carTargets, sharedRayQuery, scratch)
|
|
377
|
-
: createCarRayMiss(ray.lengthMeters);
|
|
378
|
-
writeRayVectorValues(values[index], ray.lengthMeters, roadEdge, carHit, surfaceHits, surfaceChannels);
|
|
379
|
-
}
|
|
380
|
-
return values;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
export function appendRaySensorVectorValues(target, car, snapshot, rayOptions = {}, batchContext = null) {
|
|
384
|
-
const scratch = getRayScratch(batchContext);
|
|
385
|
-
const normalized = getNormalizedRayOptions(rayOptions, scratch);
|
|
386
|
-
if (!normalized.enabled) return target;
|
|
387
|
-
const channelContext = createRayChannelContext(normalized);
|
|
388
|
-
const surfaceChannels = channelContext.surfaceChannels;
|
|
389
|
-
if (car?.destroyed || car?.outOfRace) {
|
|
390
|
-
for (let index = 0; index < normalized.rays.length; index += 1) {
|
|
391
|
-
const ray = normalized.rays[index];
|
|
392
|
-
appendRayVectorValues(
|
|
393
|
-
target,
|
|
394
|
-
ray.lengthMeters,
|
|
395
|
-
createTrackMiss(ray.lengthMeters),
|
|
396
|
-
createCarRayMiss(ray.lengthMeters),
|
|
397
|
-
{},
|
|
398
|
-
surfaceChannels,
|
|
399
|
-
);
|
|
400
|
-
}
|
|
401
|
-
return target;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
const origin = getRayOrigin(car, scratch);
|
|
405
|
-
const usesTrackContext = channelContext.hasRoadOrSurface;
|
|
406
|
-
const trackContext = !usesTrackContext
|
|
407
|
-
? null
|
|
408
|
-
: createTrackRayContext(car, snapshot, origin, normalized.precision);
|
|
409
|
-
const carTargets = channelContext.hasCar
|
|
410
|
-
? getFilteredCarTargets(car, snapshot, batchContext, scratch)
|
|
411
|
-
: [];
|
|
412
|
-
|
|
413
|
-
for (let index = 0; index < normalized.rays.length; index += 1) {
|
|
414
|
-
const ray = normalized.rays[index];
|
|
415
|
-
const angleRadians = degreesToRadians(ray.angleDegrees);
|
|
416
|
-
const vector = getScratchRayVector(scratch, index, car, angleRadians);
|
|
417
|
-
const sharedRayQuery = getSharedRayQuery(scratch, index);
|
|
418
|
-
const { roadEdge, surfaceHits } = estimateRayBands({
|
|
419
|
-
car,
|
|
420
|
-
snapshot,
|
|
421
|
-
ray,
|
|
422
|
-
angleDegrees: ray.angleDegrees,
|
|
423
|
-
origin,
|
|
424
|
-
vector,
|
|
425
|
-
normalized,
|
|
426
|
-
channelContext,
|
|
427
|
-
trackContext,
|
|
428
|
-
sharedRayQuery,
|
|
429
|
-
scratch,
|
|
430
|
-
});
|
|
431
|
-
const carHit = channelContext.hasCar
|
|
432
|
-
? estimateCarHitForRay(car, snapshot, ray, origin, vector, carTargets, sharedRayQuery, scratch)
|
|
433
|
-
: createCarRayMiss(ray.lengthMeters);
|
|
434
|
-
appendRayVectorValues(target, ray.lengthMeters, roadEdge, carHit, surfaceHits, surfaceChannels);
|
|
435
|
-
}
|
|
436
|
-
return target;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
function buildInactiveCarRays(normalized, scratch = null) {
|
|
440
|
-
const channelContext = createRayChannelContext(normalized);
|
|
441
|
-
const rays = prepareScratchArray(scratch, 'rays', 'rayPool', normalized.rays.length, () => ({})) ??
|
|
442
|
-
normalized.rays.map(() => ({}));
|
|
443
|
-
for (let index = 0; index < normalized.rays.length; index += 1) {
|
|
444
|
-
const ray = normalized.rays[index];
|
|
445
|
-
const roadEdge = createTrackMiss(ray.lengthMeters);
|
|
446
|
-
writeRichRay(
|
|
447
|
-
rays[index],
|
|
448
|
-
ray,
|
|
449
|
-
degreesToRadians(ray.angleDegrees),
|
|
450
|
-
roadEdge,
|
|
451
|
-
createCarRayMiss(ray.lengthMeters),
|
|
452
|
-
{},
|
|
453
|
-
channelContext.channels,
|
|
454
|
-
);
|
|
455
|
-
}
|
|
456
|
-
return rays;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
function createInactiveRayVectorValues(ray, surfaceChannels) {
|
|
460
|
-
return rayVectorValues(
|
|
461
|
-
ray.lengthMeters,
|
|
462
|
-
createTrackMiss(ray.lengthMeters),
|
|
463
|
-
createCarRayMiss(ray.lengthMeters),
|
|
464
|
-
{},
|
|
465
|
-
surfaceChannels,
|
|
466
|
-
);
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
function buildInactiveRayVectorValues(normalized, surfaceChannels, scratch = null) {
|
|
470
|
-
const values = prepareScratchArray(scratch, 'rayVectorValues', 'rayVectorValuePool', normalized.rays.length, () => []) ??
|
|
471
|
-
normalized.rays.map(() => []);
|
|
472
|
-
for (let index = 0; index < normalized.rays.length; index += 1) {
|
|
473
|
-
const ray = normalized.rays[index];
|
|
474
|
-
writeRayVectorValues(
|
|
475
|
-
values[index],
|
|
476
|
-
ray.lengthMeters,
|
|
477
|
-
createTrackMiss(ray.lengthMeters),
|
|
478
|
-
createCarRayMiss(ray.lengthMeters),
|
|
479
|
-
{},
|
|
480
|
-
surfaceChannels,
|
|
481
|
-
);
|
|
482
|
-
}
|
|
483
|
-
return values;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
function rayVectorValues(lengthMeters, roadEdge, carHit, surfaceHits, surfaceChannels) {
|
|
487
|
-
return writeRayVectorValues([], lengthMeters, roadEdge, carHit, surfaceHits, surfaceChannels);
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
function writeRayVectorValues(values, lengthMeters, roadEdge, carHit, surfaceHits, surfaceChannels) {
|
|
491
|
-
values.length = 0;
|
|
492
|
-
appendRayVectorValues(values, lengthMeters, roadEdge, carHit, surfaceHits, surfaceChannels);
|
|
493
|
-
return values;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
function appendRayVectorValues(values, lengthMeters, roadEdge, carHit, surfaceHits, surfaceChannels) {
|
|
497
|
-
values.push(
|
|
498
|
-
ratio(roadEdge.distanceMeters, lengthMeters),
|
|
499
|
-
roadEdge.hit ? 1 : 0,
|
|
500
|
-
roadEdge.kind === 'exit' ? 1 : 0,
|
|
501
|
-
roadEdge.kind === 'entry' ? 1 : 0,
|
|
502
|
-
ratio(carHit.distanceMeters, lengthMeters),
|
|
503
|
-
carHit.hit ? 1 : 0,
|
|
504
|
-
carHit.relativeSpeedKph / 200,
|
|
505
|
-
carHit.targetType === 'replayGhost' ? 1 : 0,
|
|
506
|
-
);
|
|
507
|
-
for (let index = 0; index < surfaceChannels.length; index += 1) {
|
|
508
|
-
const channel = surfaceChannels[index];
|
|
509
|
-
const hit = surfaceHits[channel] ?? createSurfaceMiss(lengthMeters);
|
|
510
|
-
values.push(
|
|
511
|
-
ratio(hit.distanceMeters, lengthMeters),
|
|
512
|
-
hit.hit ? 1 : 0,
|
|
513
|
-
);
|
|
514
|
-
}
|
|
515
|
-
return values;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
function ratio(value, max) {
|
|
519
|
-
const finite = Number.isFinite(value) ? value : max;
|
|
520
|
-
return Math.max(0, Math.min(1, finite / Math.max(1e-9, max)));
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
function canUseFastBatchTrainingRays(car, snapshot, trackContext) {
|
|
524
|
-
return car?.interaction?.profile === 'batch-training' &&
|
|
525
|
-
!car.inPitLane &&
|
|
526
|
-
!trackContext?.originState?.inPitLane &&
|
|
527
|
-
Array.isArray(snapshot.track?.samples) &&
|
|
528
|
-
snapshot.track.samples.length > 0 &&
|
|
529
|
-
trackContext?.originState &&
|
|
530
|
-
canUseBatchTrainingRayApproximation(snapshot.track, trackContext.originState);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
function buildFastBatchTrainingRays(car, snapshot, normalized, channelContext, origin, carTargets, trackContext, scratch = null) {
|
|
534
|
-
const rays = prepareScratchArray(scratch, 'rays', 'rayPool', normalized.rays.length, () => ({})) ??
|
|
535
|
-
normalized.rays.map(() => ({}));
|
|
536
|
-
for (let index = 0; index < normalized.rays.length; index += 1) {
|
|
537
|
-
const ray = normalized.rays[index];
|
|
538
|
-
const angleRadians = degreesToRadians(ray.angleDegrees);
|
|
539
|
-
const vector = getScratchRayVector(scratch, index, car, angleRadians);
|
|
540
|
-
const sharedRayQuery = getSharedRayQuery(scratch, index);
|
|
541
|
-
const { roadEdge, surfaceHits } = estimateRayBands({
|
|
542
|
-
car,
|
|
543
|
-
snapshot,
|
|
544
|
-
ray,
|
|
545
|
-
angleDegrees: ray.angleDegrees,
|
|
546
|
-
origin,
|
|
547
|
-
vector,
|
|
548
|
-
normalized,
|
|
549
|
-
channelContext,
|
|
550
|
-
trackContext,
|
|
551
|
-
sharedRayQuery,
|
|
552
|
-
scratch,
|
|
553
|
-
});
|
|
554
|
-
const carHit = channelContext.hasCar && carTargets.length > 0
|
|
555
|
-
? estimateCarHitForRay(car, snapshot, ray, origin, vector, carTargets, sharedRayQuery, scratch)
|
|
556
|
-
: createCarRayMiss(ray.lengthMeters);
|
|
557
|
-
|
|
558
|
-
writeRichRay(rays[index], ray, angleRadians, roadEdge, carHit, surfaceHits, channelContext.channels);
|
|
559
|
-
}
|
|
560
|
-
return rays;
|
|
561
|
-
}
|
|
6
|
+
export { createRayBatchContext } from './rayScratch.js';
|
|
7
|
+
export { buildRaySensors, buildRaySensorVectorValues, appendRaySensorVectorValues } from './rayBatch.js';
|
|
@@ -3,15 +3,6 @@ import { metersToSimUnits } from '../../simulation/units.js';
|
|
|
3
3
|
|
|
4
4
|
const RAY_BOUND_QUERY_MARGIN_METERS = 18;
|
|
5
5
|
|
|
6
|
-
export function findIndexedRayBoundaryHit(track, origin, vector, lengthMeters, offsets, target = null) {
|
|
7
|
-
const boundaries = findIndexedRayBoundaryDistances(track, origin, vector, lengthMeters, offsets, target);
|
|
8
|
-
if (!boundaries.available) return { available: false, distance: null };
|
|
9
|
-
return {
|
|
10
|
-
available: true,
|
|
11
|
-
distance: minFiniteArray(boundaries.distances),
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
|
|
15
6
|
export function findIndexedRayBoundaryDistances(track, origin, vector, lengthMeters, offsets, target = null) {
|
|
16
7
|
const result = target ?? {};
|
|
17
8
|
const finiteOffsets = target?.finiteOffsets ?? [];
|
|
@@ -173,15 +164,6 @@ function minFinite(first, second) {
|
|
|
173
164
|
return first <= second ? first : second;
|
|
174
165
|
}
|
|
175
166
|
|
|
176
|
-
function minFiniteArray(values) {
|
|
177
|
-
let minimum = Infinity;
|
|
178
|
-
for (let index = 0; index < values.length; index += 1) {
|
|
179
|
-
const value = values[index];
|
|
180
|
-
if (Number.isFinite(value) && value < minimum) minimum = value;
|
|
181
|
-
}
|
|
182
|
-
return Number.isFinite(minimum) ? minimum : null;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
167
|
function nearestSegmentId(firstDistance, firstSegmentId, secondDistance, secondSegmentId) {
|
|
186
168
|
if (!Number.isFinite(firstDistance)) return Number.isInteger(secondSegmentId) ? secondSegmentId : null;
|
|
187
169
|
if (!Number.isFinite(secondDistance)) return Number.isInteger(firstSegmentId) ? firstSegmentId : null;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { metersToSimUnits, simUnitsToMeters } from '../../simulation/units.js';
|
|
2
2
|
import { normalizeRelativeHeading } from './rayGeometry.js';
|
|
3
|
-
import {
|
|
3
|
+
import { isSelfCarTarget, nearbyDetectableTargetsForSnapshot } from './sensorTargets.js';
|
|
4
4
|
|
|
5
|
-
export function buildNearbyCars(car, snapshot, { maxCars = 6, radiusMeters = 150 } = {}) {
|
|
5
|
+
export function buildNearbyCars(car, snapshot, { maxCars = 6, radiusMeters = 150 } = {}, batchContext = null) {
|
|
6
6
|
const limit = Math.max(0, Math.floor(maxCars));
|
|
7
7
|
if (limit === 0) return [];
|
|
8
8
|
const radius = metersToSimUnits(radiusMeters);
|
|
@@ -13,11 +13,15 @@ export function buildNearbyCars(car, snapshot, { maxCars = 6, radiusMeters = 150
|
|
|
13
13
|
const rightY = Math.cos(car.heading);
|
|
14
14
|
const nearest = [];
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
const targets = batchContext?.nearbyTargets ?? nearbyDetectableTargetsForSnapshot(snapshot);
|
|
17
|
+
for (const other of targets) {
|
|
18
|
+
if (isSelfCarTarget(car, other)) continue;
|
|
17
19
|
const dx = other.x - car.x;
|
|
18
20
|
const dy = other.y - car.y;
|
|
19
21
|
const distanceSquared = dx * dx + dy * dy;
|
|
20
|
-
if (distanceSquared > radiusSquared)
|
|
22
|
+
if (distanceSquared > radiusSquared) continue;
|
|
23
|
+
const insertAt = nearestInsertIndex(nearest, distanceSquared, other.order);
|
|
24
|
+
if (insertAt >= limit) continue;
|
|
21
25
|
|
|
22
26
|
const forward = forwardX * dx + forwardY * dy;
|
|
23
27
|
const right = rightX * dx + rightY * dy;
|
|
@@ -34,19 +38,18 @@ export function buildNearbyCars(car, snapshot, { maxCars = 6, radiusMeters = 150
|
|
|
34
38
|
distanceSquared,
|
|
35
39
|
order: other.order,
|
|
36
40
|
};
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
nearest.splice(insertAt, 0, entry);
|
|
42
|
+
if (nearest.length > limit) nearest.pop();
|
|
43
|
+
}
|
|
39
44
|
|
|
40
45
|
return nearest.map(({ distanceSquared, order, ...entry }) => entry);
|
|
41
46
|
}
|
|
42
47
|
|
|
43
|
-
function
|
|
44
|
-
let
|
|
45
|
-
|
|
46
|
-
(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
nearest.splice(insertAt, 0, entry);
|
|
51
|
-
if (nearest.length > limit) nearest.pop();
|
|
48
|
+
function nearestInsertIndex(nearest, distanceSquared, order) {
|
|
49
|
+
for (let index = 0; index < nearest.length; index += 1) {
|
|
50
|
+
const candidate = nearest[index];
|
|
51
|
+
if (distanceSquared < candidate.distanceSquared ||
|
|
52
|
+
(distanceSquared === candidate.distanceSquared && order < candidate.order)) return index;
|
|
53
|
+
}
|
|
54
|
+
return nearest.length;
|
|
52
55
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { kphToSimSpeed, simSpeedToMetersPerSecond, simUnitsToMeters } from '../../simulation/units.js';
|
|
2
2
|
import { VEHICLE_GEOMETRY } from '../../simulation/vehicle/vehicleGeometry.js';
|
|
3
3
|
|
|
4
|
-
export function enrichOpponentRadar(car, nearbyCars, snapshot) {
|
|
5
|
-
|
|
4
|
+
export function enrichOpponentRadar(car, nearbyCars, snapshot, batchContext = null) {
|
|
5
|
+
if (!nearbyCars.length) return [];
|
|
6
|
+
const targetsById = batchContext?.targetsById ?? new Map([
|
|
6
7
|
...(snapshot.cars ?? []).map((entry) => [targetKey('car', entry.id), entry]),
|
|
7
8
|
...(snapshot.replayGhosts ?? []).map((entry) => [targetKey('replayGhost', entry.id), entry]),
|
|
8
9
|
]);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { metersToSimUnits } from '../../simulation/units.js';
|
|
2
|
+
import { PIT_CONNECTOR_RAY_FALLBACK_METERS } from './rayDefaults.js';
|
|
3
|
+
|
|
4
|
+
export function isNearPitConnector(track, state) {
|
|
5
|
+
const pitLane = track?.pitLane;
|
|
6
|
+
if (!pitLane?.enabled || !Number.isFinite(state?.distance)) return false;
|
|
7
|
+
const window = metersToSimUnits(PIT_CONNECTOR_RAY_FALLBACK_METERS);
|
|
8
|
+
const entryDistance = pitLane.entry?.trackDistance ?? pitLane.entry?.distanceFromStart;
|
|
9
|
+
const exitDistance = pitLane.exit?.trackDistance ?? pitLane.exit?.distanceFromStart;
|
|
10
|
+
return wrappedTrackDistance(state.distance, entryDistance, track.length) <= window ||
|
|
11
|
+
wrappedTrackDistance(state.distance, exitDistance, track.length) <= window;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function wrappedTrackDistance(first, second, totalLength) {
|
|
15
|
+
if (!Number.isFinite(first) || !Number.isFinite(second) || !Number.isFinite(totalLength) || totalLength <= 0) {
|
|
16
|
+
return Infinity;
|
|
17
|
+
}
|
|
18
|
+
const delta = Math.abs(first - second);
|
|
19
|
+
return Math.min(delta, totalLength - delta);
|
|
20
|
+
}
|