@inventure71/paddockjs 2.0.1 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +176 -1
- package/INSTALL_AND_UPDATE.md +24 -4
- package/README.md +84 -512
- package/docs/architecture.md +38 -24
- package/docs/component_inventory.md +72 -0
- package/docs/composable-layouts.md +77 -0
- package/docs/concepts.md +2 -2
- package/docs/custom_model_controller.md +6 -6
- package/docs/data-helpers.md +50 -0
- package/docs/data_contract.md +104 -27
- package/docs/getting-started.md +75 -0
- package/docs/headless-environment.md +68 -0
- package/docs/index.md +18 -4
- package/docs/learnings.md +6 -6
- package/docs/rules.md +12 -8
- package/docs/sense_contract.md +1 -1
- package/docs/system_specs.md +71 -21
- package/docs/theming.md +64 -0
- package/docs/training.md +26 -19
- package/docs/troubleshooting.md +76 -0
- package/docs/upgrading-hosts-to-3.md +42 -0
- package/examples/python/README.md +6 -0
- package/examples/python/base_policy_server.py +19 -0
- package/package.json +10 -3
- package/src/api/PaddockSimulatorController.js +150 -24
- package/src/api/runtimeTheme.js +59 -0
- package/src/app/F1SimulatorApp.js +213 -29
- package/src/app/banners/raceDataBannerController.js +71 -36
- package/src/app/banners/stewardMessageController.js +5 -4
- package/src/app/camera/cameraConstants.js +2 -0
- package/src/app/camera/cameraController.js +86 -29
- package/src/app/camera/cameraGeometry.js +12 -1
- package/src/app/domBindings.js +89 -9
- package/src/app/layoutScheduler.js +27 -0
- package/src/app/layoutSupport.js +165 -0
- package/src/app/raceOverlayClearanceSupport.js +158 -0
- package/src/app/readouts/carOverviewRenderer.js +16 -8
- package/src/app/readouts/controlStateRenderer.js +1 -1
- package/src/app/readouts/raceStatusRenderer.js +30 -21
- package/src/app/readouts/readoutFormatters.js +2 -0
- package/src/app/readouts/telemetryRenderer.js +17 -8
- package/src/app/readouts/timingTowerRenderer.js +262 -49
- package/src/app/runtime/frameLoop.js +5 -1
- package/src/app/runtime/runtimeVisibility.js +66 -5
- package/src/config/cssValues.js +16 -0
- package/src/config/defaultOptions.js +58 -38
- package/src/config/themeOptions.js +830 -0
- package/src/config/timingGapMode.js +13 -0
- package/src/config/urlOptions.js +25 -0
- package/src/data/championship.js +14 -9
- package/src/data/index.d.ts +212 -0
- package/src/data/index.js +14 -0
- package/src/environment/controllerLoop.js +17 -3
- package/src/environment/episode.js +11 -1
- package/src/environment/index.d.ts +1 -2
- package/src/environment/metrics.js +59 -16
- package/src/environment/observationVector.js +50 -33
- package/src/environment/observations.js +89 -20
- package/src/environment/options.js +1 -1
- package/src/environment/runtime.js +44 -11
- package/src/environment/scenarios.js +1 -1
- package/src/environment/sensors/carRays.js +48 -37
- package/src/environment/sensors/index.js +501 -97
- package/src/environment/sensors/indexedRayBands.js +134 -46
- package/src/environment/sensors/rayBandTrace.js +1171 -0
- package/src/environment/sensors/rayChannels.js +108 -0
- package/src/environment/sensors/rayConfig.js +71 -25
- package/src/environment/sensors/rayGeometry.js +23 -17
- package/src/environment/sensors/sensorTargets.js +67 -1
- package/src/environment/sensors/surfaceRays.js +167 -118
- package/src/environment/sensors/trackRays.js +157 -48
- package/src/index.d.ts +153 -9
- package/src/index.js +52 -3
- package/src/simulation/collisionGeometry.js +260 -96
- package/src/simulation/driver/driverController.js +1 -1
- package/src/simulation/driver/edgeRecovery.js +1 -1
- package/src/simulation/driver/racingControls.js +2 -2
- package/src/simulation/driver/rejoinControls.js +2 -2
- package/src/simulation/pit/pitFlow.js +21 -9
- package/src/simulation/pit/pitKinematics.js +2 -2
- package/src/simulation/pit/pitQueue.js +5 -3
- package/src/simulation/pit/pitRouteMovement.js +9 -4
- package/src/simulation/pit/pitRouting.js +79 -46
- package/src/simulation/pit/pitState.js +1 -0
- package/src/simulation/pit/pitTireService.js +7 -3
- package/src/simulation/race/raceFinish.js +25 -8
- package/src/simulation/race/raceOrder.js +189 -32
- package/src/simulation/race/raceProgress.js +129 -37
- package/src/simulation/race/raceSetup.js +4 -6
- package/src/simulation/race/raceStep.js +26 -14
- package/src/simulation/race/safetyCar.js +1 -1
- package/src/simulation/race/stalledDnf.js +1 -1
- package/src/simulation/raceSimulation.js +20 -17
- package/src/simulation/rules/collisionSteward.js +40 -23
- package/src/simulation/rules/rulesReview.js +77 -56
- package/src/simulation/snapshots/raceSnapshots.js +37 -0
- package/src/simulation/timing/drsTiming.js +143 -30
- package/src/simulation/timing/gapEstimation.js +25 -3
- package/src/simulation/timing/lapTelemetry.js +138 -40
- package/src/simulation/timing/raceTiming.js +5 -1
- package/src/simulation/timing/sectorPerformance.js +104 -26
- package/src/simulation/timing/timingHistory.js +229 -14
- package/src/simulation/timing/timingLines.js +65 -7
- package/src/simulation/track/pitLaneLayout.js +30 -3
- package/src/simulation/track/pitLaneState.js +218 -19
- package/src/simulation/track/spatialQueries.js +66 -33
- package/src/simulation/track/trackMath.js +91 -24
- package/src/simulation/track/trackModel.js +2 -0
- package/src/simulation/track/trackQueryIndex.js +1468 -189
- package/src/simulation/track/trackQueryPitIndex.js +138 -14
- package/src/simulation/track/trackQueryScratch.js +7 -0
- package/src/simulation/track/trackQueryStats.js +25 -0
- package/src/simulation/track/trackSnapshotJson.js +139 -0
- package/src/simulation/track/trackStatePolicy.js +249 -2
- package/src/simulation/vehicle/contactResolution.js +99 -62
- package/src/simulation/vehicle/mainTrackWheelSurface.js +73 -34
- package/src/simulation/vehicle/pitWheelSurface.js +81 -28
- package/src/simulation/vehicle/runoffResponse.js +54 -29
- package/src/simulation/vehicle/vehicleGeometry.js +245 -51
- package/src/simulation/vehicle/vehicleKinematics.js +2 -2
- package/src/simulation/vehicle/vehiclePhysics.js +45 -30
- package/src/simulation/vehicle/vehicleSnapshots.js +35 -1
- package/src/simulation/vehicle/vehicleState.js +7 -1
- package/src/simulation/vehicle/wheelSurface.js +807 -92
- package/src/simulation/warmup/runtimeWarmup.js +0 -1
- package/src/styles.css +1568 -519
- package/src/ui/bannerTemplates.js +38 -28
- package/src/ui/cameraControlsTemplate.js +16 -5
- package/src/ui/carOverviewTemplate.js +12 -5
- package/src/ui/raceCanvasTemplate.js +31 -8
- package/src/ui/raceControlsTemplate.js +27 -10
- package/src/ui/shellTemplate.js +3 -1
- package/src/ui/telemetryDrawerTemplate.js +23 -10
- package/src/ui/telemetryTemplates.js +58 -32
- package/src/ui/templateUtils.js +48 -0
- package/src/ui/timingTowerTemplate.js +29 -12
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
const RAY_CHANNEL_FLAGS = Symbol('rayChannelFlags');
|
|
2
|
+
|
|
3
|
+
export const RAY_CHANNELS = Object.freeze([
|
|
4
|
+
'roadEdge',
|
|
5
|
+
'kerb',
|
|
6
|
+
'illegalSurface',
|
|
7
|
+
'car',
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
export function createRayChannelFlags() {
|
|
11
|
+
const flags = {
|
|
12
|
+
hasRoadEdge: false,
|
|
13
|
+
hasKerb: false,
|
|
14
|
+
hasIllegalSurface: false,
|
|
15
|
+
hasCar: false,
|
|
16
|
+
surfaceChannels: [],
|
|
17
|
+
has: channelFlagsHas,
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(flags, RAY_CHANNEL_FLAGS, {
|
|
20
|
+
value: true,
|
|
21
|
+
enumerable: false,
|
|
22
|
+
});
|
|
23
|
+
return flags;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function writeRayChannelFlags(target = createRayChannelFlags(), channels = []) {
|
|
27
|
+
ensureRayChannelFlags(target);
|
|
28
|
+
target.hasRoadEdge = false;
|
|
29
|
+
target.hasKerb = false;
|
|
30
|
+
target.hasIllegalSurface = false;
|
|
31
|
+
target.hasCar = false;
|
|
32
|
+
target.surfaceChannels.length = 0;
|
|
33
|
+
|
|
34
|
+
if (isRayChannelFlags(channels)) {
|
|
35
|
+
if (channels.hasRoadEdge) target.hasRoadEdge = true;
|
|
36
|
+
if (channels.hasKerb) {
|
|
37
|
+
target.hasKerb = true;
|
|
38
|
+
target.surfaceChannels.push('kerb');
|
|
39
|
+
}
|
|
40
|
+
if (channels.hasIllegalSurface) {
|
|
41
|
+
target.hasIllegalSurface = true;
|
|
42
|
+
target.surfaceChannels.push('illegalSurface');
|
|
43
|
+
}
|
|
44
|
+
if (channels.hasCar) target.hasCar = true;
|
|
45
|
+
return target;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (Array.isArray(channels)) {
|
|
49
|
+
for (let index = 0; index < channels.length; index += 1) {
|
|
50
|
+
enableRayChannel(target, channels[index]);
|
|
51
|
+
}
|
|
52
|
+
return target;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (channels && typeof channels.has === 'function') {
|
|
56
|
+
for (let index = 0; index < RAY_CHANNELS.length; index += 1) {
|
|
57
|
+
const channel = RAY_CHANNELS[index];
|
|
58
|
+
if (channels.has(channel)) enableRayChannel(target, channel);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return target;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function isLegalRaySurface(surface) {
|
|
65
|
+
return surface === 'track' ||
|
|
66
|
+
surface === 'kerb' ||
|
|
67
|
+
surface === 'pit-entry' ||
|
|
68
|
+
surface === 'pit-lane' ||
|
|
69
|
+
surface === 'pit-exit' ||
|
|
70
|
+
surface === 'pit-box';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function ensureRayChannelFlags(target) {
|
|
74
|
+
if (!target.surfaceChannels) target.surfaceChannels = [];
|
|
75
|
+
target.has = channelFlagsHas;
|
|
76
|
+
if (!isRayChannelFlags(target)) {
|
|
77
|
+
Object.defineProperty(target, RAY_CHANNEL_FLAGS, {
|
|
78
|
+
value: true,
|
|
79
|
+
enumerable: false,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function isRayChannelFlags(value) {
|
|
85
|
+
return Boolean(value?.[RAY_CHANNEL_FLAGS]);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function enableRayChannel(target, channel) {
|
|
89
|
+
if (channel === 'roadEdge') {
|
|
90
|
+
target.hasRoadEdge = true;
|
|
91
|
+
} else if (channel === 'kerb') {
|
|
92
|
+
if (!target.hasKerb) target.surfaceChannels.push('kerb');
|
|
93
|
+
target.hasKerb = true;
|
|
94
|
+
} else if (channel === 'illegalSurface') {
|
|
95
|
+
if (!target.hasIllegalSurface) target.surfaceChannels.push('illegalSurface');
|
|
96
|
+
target.hasIllegalSurface = true;
|
|
97
|
+
} else if (channel === 'car') {
|
|
98
|
+
target.hasCar = true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function channelFlagsHas(channel) {
|
|
103
|
+
if (channel === 'roadEdge') return this.hasRoadEdge;
|
|
104
|
+
if (channel === 'kerb') return this.hasKerb;
|
|
105
|
+
if (channel === 'illegalSurface') return this.hasIllegalSurface;
|
|
106
|
+
if (channel === 'car') return this.hasCar;
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { DEFAULT_RAY_ANGLES_DEGREES } from './rayDefaults.js';
|
|
2
|
+
import { RAY_CHANNELS } from './rayChannels.js';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
const NORMALIZED_RAY_OPTIONS = Symbol('normalizedRayOptions');
|
|
5
|
+
|
|
6
|
+
export { RAY_CHANNELS } from './rayChannels.js';
|
|
4
7
|
|
|
5
|
-
export const
|
|
6
|
-
'roadEdge',
|
|
7
|
-
'kerb',
|
|
8
|
-
'illegalSurface',
|
|
9
|
-
'car',
|
|
10
|
-
]);
|
|
8
|
+
export const DEFAULT_RAY_LENGTH_METERS = 120;
|
|
11
9
|
|
|
12
10
|
export const RAY_PRECISION_MODES = Object.freeze(['driver', 'debug']);
|
|
13
11
|
|
|
@@ -26,6 +24,7 @@ export const RAY_LAYOUT_PRESETS = Object.freeze({
|
|
|
26
24
|
});
|
|
27
25
|
|
|
28
26
|
export function normalizeRayOptions(rayOptions = {}) {
|
|
27
|
+
if (rayOptions?.[NORMALIZED_RAY_OPTIONS]) return rayOptions;
|
|
29
28
|
const defaultLengthMeters = positiveNumber(
|
|
30
29
|
rayOptions.defaultLengthMeters ?? rayOptions.lengthMeters,
|
|
31
30
|
DEFAULT_RAY_LENGTH_METERS,
|
|
@@ -33,10 +32,22 @@ export function normalizeRayOptions(rayOptions = {}) {
|
|
|
33
32
|
const rays = normalizeRays(rayOptions, defaultLengthMeters);
|
|
34
33
|
const channels = normalizeRayChannels(rayOptions);
|
|
35
34
|
const precision = RAY_PRECISION_MODES.includes(rayOptions.precision) ? rayOptions.precision : 'driver';
|
|
36
|
-
const anglesDegrees = rays.
|
|
37
|
-
|
|
35
|
+
const anglesDegrees = new Array(rays.length);
|
|
36
|
+
let lengthMeters = rays.length ? 0 : defaultLengthMeters;
|
|
37
|
+
for (let index = 0; index < rays.length; index += 1) {
|
|
38
|
+
const ray = rays[index];
|
|
39
|
+
anglesDegrees[index] = ray.angleDegrees;
|
|
40
|
+
if (ray.lengthMeters > lengthMeters) lengthMeters = ray.lengthMeters;
|
|
41
|
+
}
|
|
42
|
+
let detectTrack = false;
|
|
43
|
+
let detectCars = false;
|
|
44
|
+
for (let index = 0; index < channels.length; index += 1) {
|
|
45
|
+
const channel = channels[index];
|
|
46
|
+
if (channel === 'roadEdge') detectTrack = true;
|
|
47
|
+
else if (channel === 'car') detectCars = true;
|
|
48
|
+
}
|
|
38
49
|
|
|
39
|
-
|
|
50
|
+
const normalized = {
|
|
40
51
|
...rayOptions,
|
|
41
52
|
enabled: rayOptions.enabled !== false,
|
|
42
53
|
anglesDegrees,
|
|
@@ -45,20 +56,24 @@ export function normalizeRayOptions(rayOptions = {}) {
|
|
|
45
56
|
rays,
|
|
46
57
|
channels,
|
|
47
58
|
precision,
|
|
48
|
-
detectTrack
|
|
49
|
-
detectCars
|
|
59
|
+
detectTrack,
|
|
60
|
+
detectCars,
|
|
50
61
|
};
|
|
62
|
+
Object.defineProperty(normalized, NORMALIZED_RAY_OPTIONS, {
|
|
63
|
+
value: true,
|
|
64
|
+
enumerable: false,
|
|
65
|
+
});
|
|
66
|
+
return normalized;
|
|
51
67
|
}
|
|
52
68
|
|
|
53
69
|
function normalizeRays(rayOptions, defaultLengthMeters) {
|
|
54
70
|
const configuredRays = Array.isArray(rayOptions.rays)
|
|
55
71
|
? rayOptions.rays
|
|
56
72
|
: resolvePresetRays(rayOptions.layout);
|
|
57
|
-
const source = configuredRays.length
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return source.map((ray, index) => {
|
|
73
|
+
const source = configuredRays.length ? configuredRays : normalizeAngles(rayOptions.anglesDegrees);
|
|
74
|
+
const rays = new Array(source.length);
|
|
75
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
76
|
+
const ray = source[index];
|
|
62
77
|
const angleDegrees = finiteNumber(
|
|
63
78
|
typeof ray === 'number' ? ray : ray?.angleDegrees,
|
|
64
79
|
DEFAULT_RAY_ANGLES_DEGREES[index % DEFAULT_RAY_ANGLES_DEGREES.length],
|
|
@@ -67,12 +82,13 @@ function normalizeRays(rayOptions, defaultLengthMeters) {
|
|
|
67
82
|
typeof ray === 'object' ? ray.lengthMeters : null,
|
|
68
83
|
defaultLengthMeters,
|
|
69
84
|
);
|
|
70
|
-
|
|
85
|
+
rays[index] = {
|
|
71
86
|
id: typeof ray === 'object' && ray.id ? String(ray.id) : `ray-${index}`,
|
|
72
87
|
angleDegrees,
|
|
73
88
|
lengthMeters,
|
|
74
89
|
};
|
|
75
|
-
}
|
|
90
|
+
}
|
|
91
|
+
return rays;
|
|
76
92
|
}
|
|
77
93
|
|
|
78
94
|
function resolvePresetRays(layout) {
|
|
@@ -81,16 +97,24 @@ function resolvePresetRays(layout) {
|
|
|
81
97
|
}
|
|
82
98
|
|
|
83
99
|
function normalizeAngles(value) {
|
|
84
|
-
if (!Array.isArray(value) || value.length === 0) return
|
|
85
|
-
const angles =
|
|
86
|
-
|
|
100
|
+
if (!Array.isArray(value) || value.length === 0) return copyDefaultRayAngles();
|
|
101
|
+
const angles = [];
|
|
102
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
103
|
+
const angle = Number(value[index]);
|
|
104
|
+
if (Number.isFinite(angle)) angles.push(angle);
|
|
105
|
+
}
|
|
106
|
+
return angles.length ? angles : copyDefaultRayAngles();
|
|
87
107
|
}
|
|
88
108
|
|
|
89
109
|
function normalizeRayChannels(rayOptions) {
|
|
90
110
|
if (Array.isArray(rayOptions.channels) && rayOptions.channels.length) {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
111
|
+
const channels = [];
|
|
112
|
+
for (let index = 0; index < rayOptions.channels.length; index += 1) {
|
|
113
|
+
const channel = rayOptions.channels[index];
|
|
114
|
+
if (!isKnownRayChannel(channel) || hasChannel(channels, channel)) continue;
|
|
115
|
+
channels.push(channel);
|
|
116
|
+
}
|
|
117
|
+
if (channels.length) return channels;
|
|
94
118
|
}
|
|
95
119
|
|
|
96
120
|
const channels = [];
|
|
@@ -99,6 +123,28 @@ function normalizeRayChannels(rayOptions) {
|
|
|
99
123
|
return channels;
|
|
100
124
|
}
|
|
101
125
|
|
|
126
|
+
function isKnownRayChannel(channel) {
|
|
127
|
+
for (let index = 0; index < RAY_CHANNELS.length; index += 1) {
|
|
128
|
+
if (RAY_CHANNELS[index] === channel) return true;
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function hasChannel(channels, channel) {
|
|
134
|
+
for (let index = 0; index < channels.length; index += 1) {
|
|
135
|
+
if (channels[index] === channel) return true;
|
|
136
|
+
}
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function copyDefaultRayAngles() {
|
|
141
|
+
const angles = new Array(DEFAULT_RAY_ANGLES_DEGREES.length);
|
|
142
|
+
for (let index = 0; index < DEFAULT_RAY_ANGLES_DEGREES.length; index += 1) {
|
|
143
|
+
angles[index] = DEFAULT_RAY_ANGLES_DEGREES[index];
|
|
144
|
+
}
|
|
145
|
+
return angles;
|
|
146
|
+
}
|
|
147
|
+
|
|
102
148
|
function positiveNumber(value, fallback) {
|
|
103
149
|
const number = Number(value);
|
|
104
150
|
return Number.isFinite(number) && number > 0 ? number : fallback;
|
|
@@ -21,13 +21,31 @@ export function pointOnRay(origin, ray, distance) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export function intersectAxisAlignedBoxRay(origin, ray, halfLength, halfWidth) {
|
|
24
|
+
return intersectAxisAlignedBoxRayScalars(origin.x, origin.y, ray.x, ray.y, halfLength, halfWidth);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function intersectAxisAlignedBoxRayScalars(originX, originY, rayX, rayY, halfLength, halfWidth) {
|
|
24
28
|
let tMin = -Infinity;
|
|
25
29
|
let tMax = Infinity;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
|
|
31
|
+
if (Math.abs(rayX) < 1e-9) {
|
|
32
|
+
if (originX < -halfLength || originX > halfLength) return null;
|
|
33
|
+
} else {
|
|
34
|
+
const first = (-halfLength - originX) / rayX;
|
|
35
|
+
const second = (halfLength - originX) / rayX;
|
|
36
|
+
tMin = Math.max(tMin, Math.min(first, second));
|
|
37
|
+
tMax = Math.min(tMax, Math.max(first, second));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (Math.abs(rayY) < 1e-9) {
|
|
41
|
+
if (originY < -halfWidth || originY > halfWidth) return null;
|
|
42
|
+
} else {
|
|
43
|
+
const first = (-halfWidth - originY) / rayY;
|
|
44
|
+
const second = (halfWidth - originY) / rayY;
|
|
45
|
+
tMin = Math.max(tMin, Math.min(first, second));
|
|
46
|
+
tMax = Math.min(tMax, Math.max(first, second));
|
|
47
|
+
}
|
|
48
|
+
|
|
31
49
|
if (tMax < 0 || tMin > tMax) return null;
|
|
32
50
|
return Math.max(0, tMin);
|
|
33
51
|
}
|
|
@@ -46,15 +64,3 @@ export function normalizeRelativeHeading(angle) {
|
|
|
46
64
|
while (value < -Math.PI) value += Math.PI * 2;
|
|
47
65
|
return value;
|
|
48
66
|
}
|
|
49
|
-
|
|
50
|
-
function intersectSlab(origin, direction, min, max) {
|
|
51
|
-
if (Math.abs(direction) < 1e-9) {
|
|
52
|
-
return origin >= min && origin <= max ? { min: -Infinity, max: Infinity } : null;
|
|
53
|
-
}
|
|
54
|
-
const first = (min - origin) / direction;
|
|
55
|
-
const second = (max - origin) / direction;
|
|
56
|
-
return {
|
|
57
|
-
min: Math.min(first, second),
|
|
58
|
-
max: Math.max(first, second),
|
|
59
|
-
};
|
|
60
|
-
}
|
|
@@ -14,7 +14,10 @@ export function nearbyDetectableTargets(self, snapshot) {
|
|
|
14
14
|
return nearbyDetectableTargetsForSnapshot(snapshot).filter((target) => !isSelfCarTarget(self, target));
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export function rayDetectableTargetsForSnapshot(snapshot) {
|
|
17
|
+
export function rayDetectableTargetsForSnapshot(snapshot, scratch = null) {
|
|
18
|
+
if (scratch) {
|
|
19
|
+
return writeRayDetectableTargetsForSnapshot(snapshot, scratch);
|
|
20
|
+
}
|
|
18
21
|
return [
|
|
19
22
|
...detectableCars(snapshot, isRayDetectable),
|
|
20
23
|
...detectableReplayGhosts(snapshot, 'detectableByRays'),
|
|
@@ -45,6 +48,69 @@ function detectableCars(snapshot, predicate) {
|
|
|
45
48
|
}));
|
|
46
49
|
}
|
|
47
50
|
|
|
51
|
+
function writeRayDetectableTargetsForSnapshot(snapshot, scratch) {
|
|
52
|
+
const targets = scratch.rayTargets ?? [];
|
|
53
|
+
const pool = scratch.rayTargetPool ?? [];
|
|
54
|
+
scratch.rayTargets = targets;
|
|
55
|
+
scratch.rayTargetPool = pool;
|
|
56
|
+
let count = 0;
|
|
57
|
+
let carOrder = 0;
|
|
58
|
+
const cars = snapshot.cars ?? [];
|
|
59
|
+
for (let index = 0; index < cars.length; index += 1) {
|
|
60
|
+
const car = cars[index];
|
|
61
|
+
if (!isRayDetectable(car)) continue;
|
|
62
|
+
const target = pool[count] ?? {};
|
|
63
|
+
pool[count] = target;
|
|
64
|
+
writeCarTarget(target, car, carOrder);
|
|
65
|
+
targets[count] = target;
|
|
66
|
+
count += 1;
|
|
67
|
+
carOrder += 1;
|
|
68
|
+
}
|
|
69
|
+
let ghostOrder = 0;
|
|
70
|
+
const carCount = cars.length;
|
|
71
|
+
const replayGhosts = snapshot.replayGhosts ?? [];
|
|
72
|
+
for (let index = 0; index < replayGhosts.length; index += 1) {
|
|
73
|
+
const ghost = replayGhosts[index];
|
|
74
|
+
if (ghost.sensors?.detectableByRays !== true) continue;
|
|
75
|
+
const target = pool[count] ?? {};
|
|
76
|
+
pool[count] = target;
|
|
77
|
+
writeReplayGhostTarget(target, ghost, carCount + ghostOrder);
|
|
78
|
+
targets[count] = target;
|
|
79
|
+
count += 1;
|
|
80
|
+
ghostOrder += 1;
|
|
81
|
+
}
|
|
82
|
+
targets.length = count;
|
|
83
|
+
return targets;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function writeCarTarget(target, car, order) {
|
|
87
|
+
target.entityType = SENSOR_TARGET_CAR;
|
|
88
|
+
target.id = car.id;
|
|
89
|
+
target.x = car.x;
|
|
90
|
+
target.y = car.y;
|
|
91
|
+
target.heading = car.heading;
|
|
92
|
+
target.velocityX = car.velocityX;
|
|
93
|
+
target.velocityY = car.velocityY;
|
|
94
|
+
target.speedKph = car.speedKph ?? 0;
|
|
95
|
+
target.lap = car.lap;
|
|
96
|
+
target.order = order;
|
|
97
|
+
return target;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function writeReplayGhostTarget(target, ghost, order) {
|
|
101
|
+
target.entityType = SENSOR_TARGET_REPLAY_GHOST;
|
|
102
|
+
target.id = ghost.id;
|
|
103
|
+
target.x = ghost.x;
|
|
104
|
+
target.y = ghost.y;
|
|
105
|
+
target.heading = ghost.heading;
|
|
106
|
+
target.velocityX = undefined;
|
|
107
|
+
target.velocityY = undefined;
|
|
108
|
+
target.speedKph = ghost.speedKph ?? 0;
|
|
109
|
+
target.lap = null;
|
|
110
|
+
target.order = order;
|
|
111
|
+
return target;
|
|
112
|
+
}
|
|
113
|
+
|
|
48
114
|
function detectableReplayGhosts(snapshot, sensorFlag) {
|
|
49
115
|
const carCount = snapshot.cars?.length ?? 0;
|
|
50
116
|
return (snapshot.replayGhosts ?? [])
|