feat(materials): free rotate session overlays and show save progress

Match NPC drag-to-rotate on the material frame during playback, and change the add/edit Save button to Saving… while import runs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-07-21 14:56:42 +08:00
parent 0cab7ce7ca
commit c1c332364c
8 changed files with 238 additions and 67 deletions
+13 -2
View File
@@ -8,6 +8,8 @@ export type MaterialsOverlayLayout = {
cy: number;
/** Масштаб относительно «базового contain» (1 = по умолчанию). */
scale: number;
/** Свободный угол поворота на сессии, градусы. */
rotationDeg: number;
};
export type MaterialsZoomTool = 'zoomIn' | 'zoomOut' | null;
@@ -24,21 +26,30 @@ export const DEFAULT_MATERIALS_OVERLAY_LAYOUT: MaterialsOverlayLayout = {
cx: 0.5,
cy: 0.5,
scale: 1,
rotationDeg: 0,
};
export type MaterialsOverlayEvent =
| { kind: 'show'; materialId: MaterialId }
| { kind: 'show'; materialId: MaterialId; rotationDeg?: number }
| { kind: 'hide' }
| { kind: 'toggle'; materialId: MaterialId }
| { kind: 'toggle'; materialId: MaterialId; rotationDeg?: number }
| { kind: 'layout.set'; layout: MaterialsOverlayLayout }
| { kind: 'zoomTool.set'; tool: MaterialsZoomTool }
| { kind: 'zoomAt'; nx: number; ny: number };
/** Нормализация угла в диапазон [0, 360). */
export function normalizeMaterialsRotation(deg: number): number {
if (!Number.isFinite(deg)) return 0;
const n = deg % 360;
return n < 0 ? n + 360 : n;
}
export function clampMaterialsLayout(layout: MaterialsOverlayLayout): MaterialsOverlayLayout {
return {
cx: Math.min(1.2, Math.max(-0.2, layout.cx)),
cy: Math.min(1.2, Math.max(-0.2, layout.cy)),
scale: Math.min(8, Math.max(0.15, layout.scale)),
rotationDeg: normalizeMaterialsRotation(layout.rotationDeg ?? 0),
};
}