Add whitebox selection mode support in editor store and selection logic

This commit is contained in:
2026-04-04 20:07:17 +02:00
parent 2c39553fdd
commit fb10015fdf
2 changed files with 106 additions and 4 deletions

View File

@@ -1,7 +1,12 @@
import { CommandHistory } from "../commands/command-history"; import { CommandHistory } from "../commands/command-history";
import type { CommandContext, EditorCommand } from "../commands/command"; import type { CommandContext, EditorCommand } from "../commands/command";
import { areEditorSelectionsEqual, type EditorSelection } from "../core/selection"; import {
areEditorSelectionsEqual,
normalizeSelectionForWhiteboxSelectionMode,
type EditorSelection
} from "../core/selection";
import type { ToolMode } from "../core/tool-mode"; import type { ToolMode } from "../core/tool-mode";
import { type WhiteboxSelectionMode } from "../core/whitebox-selection-mode";
import { import {
areTransformSessionsEqual, areTransformSessionsEqual,
cloneTransformSession, cloneTransformSession,
@@ -45,6 +50,7 @@ import {
export interface EditorStoreState { export interface EditorStoreState {
document: SceneDocument; document: SceneDocument;
selection: EditorSelection; selection: EditorSelection;
whiteboxSelectionMode: WhiteboxSelectionMode;
toolMode: ToolMode; toolMode: ToolMode;
viewportLayoutMode: ViewportLayoutMode; viewportLayoutMode: ViewportLayoutMode;
activeViewportPanelId: ViewportPanelId; activeViewportPanelId: ViewportPanelId;
@@ -72,6 +78,7 @@ export type EditorDraftLoadResult = LoadSceneDocumentDraftResult;
export class EditorStore { export class EditorStore {
private document: SceneDocument; private document: SceneDocument;
private selection: EditorSelection = { kind: "none" }; private selection: EditorSelection = { kind: "none" };
private whiteboxSelectionMode: WhiteboxSelectionMode = "object";
private toolMode: ToolMode = "select"; private toolMode: ToolMode = "select";
private viewportLayoutMode: ViewportLayoutMode; private viewportLayoutMode: ViewportLayoutMode;
private activeViewportPanelId: ViewportPanelId; private activeViewportPanelId: ViewportPanelId;
@@ -344,6 +351,23 @@ export class EditorStore {
this.emit(); this.emit();
} }
setWhiteboxSelectionMode(mode: WhiteboxSelectionMode) {
if (this.whiteboxSelectionMode === mode) {
return;
}
if (this.viewportTransientState.transformSession.kind !== "none") {
this.viewportTransientState = {
...this.viewportTransientState,
transformSession: createInactiveTransformSession()
};
}
this.whiteboxSelectionMode = mode;
this.selection = normalizeSelectionForWhiteboxSelectionMode(this.selection, mode);
this.emit();
}
executeCommand(command: EditorCommand) { executeCommand(command: EditorCommand) {
if (this.viewportTransientState.transformSession.kind !== "none") { if (this.viewportTransientState.transformSession.kind !== "none") {
this.viewportTransientState = { this.viewportTransientState = {
@@ -412,6 +436,7 @@ export class EditorStore {
replaceDocument(document: SceneDocument, resetHistory = true) { replaceDocument(document: SceneDocument, resetHistory = true) {
this.document = document; this.document = document;
this.selection = { kind: "none" }; this.selection = { kind: "none" };
this.whiteboxSelectionMode = "object";
this.toolMode = "select"; this.toolMode = "select";
this.previousEditingToolMode = "select"; this.previousEditingToolMode = "select";
this.viewportTransientState = createDefaultViewportTransientState(); this.viewportTransientState = createDefaultViewportTransientState();
@@ -499,6 +524,7 @@ export class EditorStore {
return { return {
document: this.document, document: this.document,
selection: this.selection, selection: this.selection,
whiteboxSelectionMode: this.whiteboxSelectionMode,
toolMode: this.toolMode, toolMode: this.toolMode,
viewportLayoutMode: this.viewportLayoutMode, viewportLayoutMode: this.viewportLayoutMode,
activeViewportPanelId: this.activeViewportPanelId, activeViewportPanelId: this.activeViewportPanelId,

View File

@@ -1,9 +1,12 @@
import type { BoxFaceId } from "../document/brushes"; import type { WhiteboxSelectionMode } from "./whitebox-selection-mode";
import type { BoxEdgeId, BoxFaceId, BoxVertexId } from "../document/brushes";
export type EditorSelection = export type EditorSelection =
| { kind: "none" } | { kind: "none" }
| { kind: "brushes"; ids: string[] } | { kind: "brushes"; ids: string[] }
| { kind: "brushFace"; brushId: string; faceId: BoxFaceId } | { kind: "brushFace"; brushId: string; faceId: BoxFaceId }
| { kind: "brushEdge"; brushId: string; edgeId: BoxEdgeId }
| { kind: "brushVertex"; brushId: string; vertexId: BoxVertexId }
| { kind: "entities"; ids: string[] } | { kind: "entities"; ids: string[] }
| { kind: "modelInstances"; ids: string[] }; | { kind: "modelInstances"; ids: string[] };
@@ -22,6 +25,22 @@ export function cloneEditorSelection(selection: EditorSelection): EditorSelectio
}; };
} }
if (selection.kind === "brushEdge") {
return {
kind: "brushEdge",
brushId: selection.brushId,
edgeId: selection.edgeId
};
}
if (selection.kind === "brushVertex") {
return {
kind: "brushVertex",
brushId: selection.brushId,
vertexId: selection.vertexId
};
}
return { return {
kind: selection.kind, kind: selection.kind,
ids: [...selection.ids] ids: [...selection.ids]
@@ -38,6 +57,10 @@ export function areEditorSelectionsEqual(left: EditorSelection, right: EditorSel
return true; return true;
case "brushFace": case "brushFace":
return right.kind === "brushFace" && left.brushId === right.brushId && left.faceId === right.faceId; return right.kind === "brushFace" && left.brushId === right.brushId && left.faceId === right.faceId;
case "brushEdge":
return right.kind === "brushEdge" && left.brushId === right.brushId && left.edgeId === right.edgeId;
case "brushVertex":
return right.kind === "brushVertex" && left.brushId === right.brushId && left.vertexId === right.vertexId;
case "brushes": case "brushes":
case "entities": case "entities":
case "modelInstances": case "modelInstances":
@@ -46,7 +69,7 @@ export function areEditorSelectionsEqual(left: EditorSelection, right: EditorSel
} }
export function getSingleSelectedBrushId(selection: EditorSelection): string | null { export function getSingleSelectedBrushId(selection: EditorSelection): string | null {
if (selection.kind === "brushFace") { if (selection.kind === "brushFace" || selection.kind === "brushEdge" || selection.kind === "brushVertex") {
return selection.brushId; return selection.brushId;
} }
@@ -65,6 +88,22 @@ export function getSelectedBrushFaceId(selection: EditorSelection): BoxFaceId |
return selection.faceId; return selection.faceId;
} }
export function getSelectedBrushEdgeId(selection: EditorSelection): BoxEdgeId | null {
if (selection.kind !== "brushEdge") {
return null;
}
return selection.edgeId;
}
export function getSelectedBrushVertexId(selection: EditorSelection): BoxVertexId | null {
if (selection.kind !== "brushVertex") {
return null;
}
return selection.vertexId;
}
export function getSingleSelectedEntityId(selection: EditorSelection): string | null { export function getSingleSelectedEntityId(selection: EditorSelection): string | null {
if (selection.kind !== "entities" || selection.ids.length !== 1) { if (selection.kind !== "entities" || selection.ids.length !== 1) {
return null; return null;
@@ -84,7 +123,8 @@ export function getSingleSelectedModelInstanceId(selection: EditorSelection): st
export function isBrushSelected(selection: EditorSelection, brushId: string): boolean { export function isBrushSelected(selection: EditorSelection, brushId: string): boolean {
return ( return (
(selection.kind === "brushes" && selection.ids.includes(brushId)) || (selection.kind === "brushes" && selection.ids.includes(brushId)) ||
(selection.kind === "brushFace" && selection.brushId === brushId) ((selection.kind === "brushFace" || selection.kind === "brushEdge" || selection.kind === "brushVertex") &&
selection.brushId === brushId)
); );
} }
@@ -92,6 +132,42 @@ export function isBrushFaceSelected(selection: EditorSelection, brushId: string,
return selection.kind === "brushFace" && selection.brushId === brushId && selection.faceId === faceId; return selection.kind === "brushFace" && selection.brushId === brushId && selection.faceId === faceId;
} }
export function isBrushEdgeSelected(selection: EditorSelection, brushId: string, edgeId: BoxEdgeId): boolean {
return selection.kind === "brushEdge" && selection.brushId === brushId && selection.edgeId === edgeId;
}
export function isBrushVertexSelected(selection: EditorSelection, brushId: string, vertexId: BoxVertexId): boolean {
return selection.kind === "brushVertex" && selection.brushId === brushId && selection.vertexId === vertexId;
}
export function isModelInstanceSelected(selection: EditorSelection, modelInstanceId: string): boolean { export function isModelInstanceSelected(selection: EditorSelection, modelInstanceId: string): boolean {
return selection.kind === "modelInstances" && selection.ids.includes(modelInstanceId); return selection.kind === "modelInstances" && selection.ids.includes(modelInstanceId);
} }
export function normalizeSelectionForWhiteboxSelectionMode(selection: EditorSelection, mode: WhiteboxSelectionMode): EditorSelection {
switch (selection.kind) {
case "brushFace":
return mode === "face"
? selection
: {
kind: "brushes",
ids: [selection.brushId]
};
case "brushEdge":
return mode === "edge"
? selection
: {
kind: "brushes",
ids: [selection.brushId]
};
case "brushVertex":
return mode === "vertex"
? selection
: {
kind: "brushes",
ids: [selection.brushId]
};
default:
return selection;
}
}