feat(map): control ghost pin movement speed

This commit is contained in:
DCCONSTRUCTIONS
2026-07-29 18:53:00 +03:00
parent 0c3855eac5
commit 2678295ca7
9 changed files with 121 additions and 8 deletions
@@ -9,17 +9,23 @@ export function generateGhostPins({
anchor,
count,
radiusMeters,
maximumSpeedMetersPerSecond,
random = Math.random,
timestamp = Date.now(),
}: {
anchor: MapGeoPoint;
count: number;
radiusMeters: number;
maximumSpeedMetersPerSecond: number;
random?: () => number;
timestamp?: number;
}): MapGhostPin[] {
const safeCount = Math.min(100, Math.max(1, Math.round(count)));
const safeRadius = Math.min(100_000, Math.max(100, radiusMeters));
const safeMaximumSpeed = Math.min(
100,
Math.max(0.1, maximumSpeedMetersPerSecond),
);
const labels = new Set<string>();
return Array.from({ length: safeCount }, (_, index) => {
const distance = safeRadius * Math.sqrt(clampUnit(random()));
@@ -36,7 +42,8 @@ export function generateGhostPins({
label,
...position,
headingDegrees: clampUnit(random()) * 360,
speedMetersPerSecond: 3 + clampUnit(random()) * 15,
speedMetersPerSecond:
safeMaximumSpeed * (0.2 + clampUnit(random()) * 0.8),
};
});
}
+24 -4
View File
@@ -59,6 +59,7 @@ export interface MapGhostPinPresentation {
export interface MapGhostPinConfiguration {
count: number;
radiusMeters: number;
maximumSpeedMetersPerSecond: number;
simulationEnabled: boolean;
anchor: MapGeoPoint | null;
positions: MapGhostPin[];
@@ -226,6 +227,7 @@ export function defaultGhostPinConfiguration(): MapGhostPinConfiguration {
return {
count: 12,
radiusMeters: 5_000,
maximumSpeedMetersPerSecond: 18,
simulationEnabled: false,
anchor: null,
positions: [],
@@ -341,6 +343,8 @@ export function encodeMapViewPut(document: MapViewDocument): unknown {
ghost_pins: {
count: document.view.ghostPins.count,
radius_meters: document.view.ghostPins.radiusMeters,
maximum_speed_meters_per_second:
document.view.ghostPins.maximumSpeedMetersPerSecond,
simulation_enabled: document.view.ghostPins.simulationEnabled,
anchor: document.view.ghostPins.anchor
? {
@@ -466,6 +470,7 @@ function decodeGhostPinConfiguration(value: unknown): MapGhostPinConfiguration {
[
"count",
"radius_meters",
"maximum_speed_meters_per_second",
"simulation_enabled",
"anchor",
"positions",
@@ -483,6 +488,23 @@ function decodeGhostPinConfiguration(value: unknown): MapGhostPinConfiguration {
if (positions.length > 100) {
throw new Error("ghost_pins.positions превышает лимит.");
}
const maximumSpeedMetersPerSecond = requireNumber(
configuration.maximum_speed_meters_per_second,
"ghost_pins.maximum_speed_meters_per_second",
0.1,
100,
);
const decodedPositions = positions.map((pin, index) => decodeGhostPin(
pin,
`ghost_pins.positions[${index}]`,
));
if (
decodedPositions.some(
(pin) => pin.speedMetersPerSecond > maximumSpeedMetersPerSecond,
)
) {
throw new Error("Скорость Ghost Pin превышает настроенный максимум.");
}
return {
count: requireInteger(configuration.count, "ghost_pins.count", 1, 100),
radiusMeters: requireNumber(
@@ -491,15 +513,13 @@ function decodeGhostPinConfiguration(value: unknown): MapGhostPinConfiguration {
100,
100_000,
),
maximumSpeedMetersPerSecond,
simulationEnabled: requireBoolean(
configuration.simulation_enabled,
"ghost_pins.simulation_enabled",
),
anchor,
positions: positions.map((pin, index) => decodeGhostPin(
pin,
`ghost_pins.positions[${index}]`,
)),
positions: decodedPositions,
presentation: decodeGhostPinPresentation(configuration.presentation),
};
}