fix(editor): зажим крена маршрута к ±45°

Бэкенд считает геометрию видимости/маршрута только в |крен| ≤ 45°,
поэтому предел снижен с 89° до 45°. Введён общий clampRoll (зажим +
округление до 0.1°), применён при ручной правке крена (кнопки ±,
прямой ввод) и при загрузке сохранённого маршрута в окно построения
(легаси-маршрут вне ±45° зажимается).
This commit is contained in:
Дмитрий Соловьев
2026-06-08 13:59:53 +03:00
parent ff0b2639c9
commit 2c8b317c42
@@ -74,8 +74,15 @@ const SHOOT_WINDOW_MS = 6 * 60 * 1000;
const DEFAULT_CAPTURE_ANGLE_DEG = 1.5;
/** Дефолтная длительность строящегося маршрута съёмки (мс). */
const DEFAULT_ROUTE_DURATION_MS = 30_000;
/** Предел крена (gamma) маршрута при ручной правке, градусы. */
const MAX_ROUTE_ROLL_DEG = 89;
/**
* Предел крена (gamma) маршрута, градусы. Параметры видимости/маршрута бэкенд считает только
* в диапазоне |крен| ≤ 45°; за его пределами геометрия маршрута не определена, поэтому жёстко
* зажимаем здесь — и при ручной правке, и при загрузке сохранённого маршрута в окно построения.
*/
const MAX_ROUTE_ROLL_DEG = 45;
/** Крен с зажимом в допустимый диапазон и округлением до 1 знака (как при ручном вводе). */
const clampRoll = (deg: number): number =>
Math.round(Math.max(-MAX_ROUTE_ROLL_DEG, Math.min(MAX_ROUTE_ROLL_DEG, deg)) * 10) / 10;
/** Допустимый выход окна маршрута за пределы окна видимости, мс (±30 минут). */
const ROUTE_TIME_MARGIN_MS = 30 * 60_000;
const EDITOR_MAP_LAYERS: Tgu2DMapLayersState = {
@@ -421,15 +428,15 @@ export function TguEditorTab({ selectedSpacecraftId, selectedPlan, appliedRange,
if (prefill) {
editShootPrefillRef.current = null;
setRouteWindow({ startMs: prefill.startMs, endMs: prefill.endMs });
setRouteRoll(Math.round(prefill.roll * 10) / 10);
// Сохранённый маршрут мог быть с креном вне ±45° (легаси) — зажимаем при загрузке в окно.
setRouteRoll(clampRoll(prefill.roll));
return;
}
const passDurationMs = selectedShootPass.endMs - selectedShootPass.startMs;
const half = Math.min(DEFAULT_ROUTE_DURATION_MS, passDurationMs) / 2;
const mid = (selectedShootPass.startMs + selectedShootPass.endMs) / 2;
setRouteWindow({ startMs: mid - half, endMs: mid + half });
// Крен — не точнее одного знака после запятой (как и при ручном вводе).
setRouteRoll(Math.round((selectedShootPass.rollDeg ?? 0) * 10) / 10);
setRouteRoll(clampRoll(selectedShootPass.rollDeg ?? 0));
}, [rightMode, selectedShootPass]);
useEffect(() => {
@@ -1325,14 +1332,10 @@ export function TguEditorTab({ selectedSpacecraftId, selectedPlan, appliedRange,
})
}
onRouteRollChange={(deltaDeg) =>
setRouteRoll((current) =>
Math.max(-MAX_ROUTE_ROLL_DEG, Math.min(MAX_ROUTE_ROLL_DEG, (current ?? 0) + deltaDeg))
)
setRouteRoll((current) => clampRoll((current ?? 0) + deltaDeg))
}
onRouteRollSet={(deg) =>
setRouteRoll((current) =>
Number.isFinite(deg) ? Math.max(-MAX_ROUTE_ROLL_DEG, Math.min(MAX_ROUTE_ROLL_DEG, deg)) : current
)
setRouteRoll((current) => (Number.isFinite(deg) ? clampRoll(deg) : current))
}
onSelectPass={(pass) => {
setSelectedShootPass(pass);