From 92e4c2eeafde3cd0ce1bc8581fda5a1f1560f8b2 Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Fri, 3 Apr 2026 02:10:45 +0200 Subject: [PATCH] Add transform session management to EditorStore and ViewportTransientState --- src/app/editor-store.ts | 78 ++++++++++++++++++- .../viewport-transient-state.ts | 21 ++++- 2 files changed, 95 insertions(+), 4 deletions(-) diff --git a/src/app/editor-store.ts b/src/app/editor-store.ts index f585552a..e53130a8 100644 --- a/src/app/editor-store.ts +++ b/src/app/editor-store.ts @@ -1,7 +1,14 @@ import { CommandHistory } from "../commands/command-history"; import type { CommandContext, EditorCommand } from "../commands/command"; -import type { EditorSelection } from "../core/selection"; +import { areEditorSelectionsEqual, type EditorSelection } from "../core/selection"; import type { ToolMode } from "../core/tool-mode"; +import { + areTransformSessionsEqual, + cloneTransformSession, + createInactiveTransformSession, + type TransformAxis, + type TransformSessionState +} from "../core/transform-session"; import { createEmptySceneDocument, type SceneDocument } from "../document/scene-document"; import { DEFAULT_SCENE_DRAFT_STORAGE_KEY, @@ -129,7 +136,17 @@ export class EditorStore { this.toolMode = toolMode; if (!isViewportToolPreviewCompatible(toolMode, this.viewportTransientState.toolPreview)) { - this.viewportTransientState = createDefaultViewportTransientState(); + this.viewportTransientState = { + ...this.viewportTransientState, + toolPreview: createDefaultViewportTransientState().toolPreview + }; + } + + if (toolMode !== "select" && this.viewportTransientState.transformSession.kind !== "none") { + this.viewportTransientState = { + ...this.viewportTransientState, + transformSession: createInactiveTransformSession() + }; } this.emit(); @@ -235,7 +252,55 @@ export class EditorStore { return; } - this.viewportTransientState = createDefaultViewportTransientState(); + this.viewportTransientState = { + ...this.viewportTransientState, + toolPreview: createDefaultViewportTransientState().toolPreview + }; + this.emit(); + } + + setTransformSession(transformSession: TransformSessionState) { + const nextTransformSession = cloneTransformSession(transformSession); + + if (areTransformSessionsEqual(this.viewportTransientState.transformSession, nextTransformSession)) { + return; + } + + this.viewportTransientState = { + ...this.viewportTransientState, + transformSession: nextTransformSession + }; + this.emit(); + } + + clearTransformSession() { + if (this.viewportTransientState.transformSession.kind === "none") { + return; + } + + this.viewportTransientState = { + ...this.viewportTransientState, + transformSession: createInactiveTransformSession() + }; + this.emit(); + } + + setTransformAxisConstraint(axisConstraint: TransformAxis | null) { + if (this.viewportTransientState.transformSession.kind !== "active") { + return; + } + + if (this.viewportTransientState.transformSession.axisConstraint === axisConstraint) { + return; + } + + this.viewportTransientState = { + ...this.viewportTransientState, + transformSession: { + ...cloneTransformSession(this.viewportTransientState.transformSession), + axisConstraint + } + }; this.emit(); } @@ -268,6 +333,13 @@ export class EditorStore { } setSelection(selection: EditorSelection) { + if (this.viewportTransientState.transformSession.kind === "active" && !areEditorSelectionsEqual(this.selection, selection)) { + this.viewportTransientState = { + ...this.viewportTransientState, + transformSession: createInactiveTransformSession() + }; + } + this.selection = selection; this.emit(); } diff --git a/src/viewport-three/viewport-transient-state.ts b/src/viewport-three/viewport-transient-state.ts index 9c8fc65b..c1339f4f 100644 --- a/src/viewport-three/viewport-transient-state.ts +++ b/src/viewport-three/viewport-transient-state.ts @@ -1,5 +1,11 @@ import type { Vec3 } from "../core/vector"; import type { ToolMode } from "../core/tool-mode"; +import { + areTransformSessionsEqual, + cloneTransformSession, + createInactiveTransformSession, + type TransformSessionState +} from "../core/transform-session"; import type { EntityKind } from "../entities/entity-instances"; import type { ViewportPanelId } from "./viewport-layout"; @@ -28,13 +34,15 @@ export type ViewportToolPreview = CreationViewportToolPreview | { kind: "none" } export interface ViewportTransientState { toolPreview: ViewportToolPreview; + transformSession: TransformSessionState; } export function createDefaultViewportTransientState(): ViewportTransientState { return { toolPreview: { kind: "none" - } + }, + transformSession: createInactiveTransformSession() }; } @@ -110,3 +118,14 @@ export function isViewportToolPreviewCompatible(toolMode: ToolMode, toolPreview: return toolMode === "create" && toolPreview.kind === "create"; } + +export function cloneViewportTransientState(transientState: ViewportTransientState): ViewportTransientState { + return { + toolPreview: cloneViewportToolPreview(transientState.toolPreview), + transformSession: cloneTransformSession(transientState.transformSession) + }; +} + +export function areViewportTransientStatesEqual(left: ViewportTransientState, right: ViewportTransientState): boolean { + return areViewportToolPreviewsEqual(left.toolPreview, right.toolPreview) && areTransformSessionsEqual(left.transformSession, right.transformSession); +}