2026-04-04 20:07:17 +02:00
|
|
|
import type { WhiteboxSelectionMode } from "./whitebox-selection-mode";
|
2026-04-15 07:43:30 +02:00
|
|
|
import type {
|
|
|
|
|
WhiteboxEdgeId,
|
|
|
|
|
WhiteboxFaceId,
|
|
|
|
|
WhiteboxVertexId
|
|
|
|
|
} from "../document/brushes";
|
2026-03-31 02:33:18 +02:00
|
|
|
|
2026-03-31 01:29:35 +02:00
|
|
|
export type EditorSelection =
|
|
|
|
|
| { kind: "none" }
|
|
|
|
|
| { kind: "brushes"; ids: string[] }
|
2026-04-15 07:43:30 +02:00
|
|
|
| { kind: "brushFace"; brushId: string; faceId: WhiteboxFaceId }
|
|
|
|
|
| { kind: "brushEdge"; brushId: string; edgeId: WhiteboxEdgeId }
|
|
|
|
|
| { kind: "brushVertex"; brushId: string; vertexId: WhiteboxVertexId }
|
2026-04-18 19:47:48 +02:00
|
|
|
| { kind: "terrains"; ids: string[] }
|
2026-04-13 21:21:40 +02:00
|
|
|
| { kind: "paths"; ids: string[] }
|
2026-04-13 22:24:31 +02:00
|
|
|
| { kind: "pathPoint"; pathId: string; pointId: string }
|
2026-05-12 22:05:10 +02:00
|
|
|
| { kind: "pathPoints"; pathId: string; pointIds: string[] }
|
2026-03-31 01:29:35 +02:00
|
|
|
| { kind: "entities"; ids: string[] }
|
|
|
|
|
| { kind: "modelInstances"; ids: string[] };
|
2026-03-31 02:02:34 +02:00
|
|
|
|
2026-04-15 14:38:55 +02:00
|
|
|
export type EditorSelectionWithIds = Extract<EditorSelection, { ids: string[] }>;
|
|
|
|
|
export type SameKindMultiSelectableEditorSelection = Extract<
|
|
|
|
|
EditorSelection,
|
|
|
|
|
| { kind: "brushes"; ids: string[] }
|
|
|
|
|
| { kind: "entities"; ids: string[] }
|
|
|
|
|
| { kind: "modelInstances"; ids: string[] }
|
|
|
|
|
>;
|
|
|
|
|
|
|
|
|
|
export function isEditorSelectionWithIds(
|
|
|
|
|
selection: EditorSelection
|
|
|
|
|
): selection is EditorSelectionWithIds {
|
|
|
|
|
return "ids" in selection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function isSameKindMultiSelectableSelection(
|
|
|
|
|
selection: EditorSelection
|
|
|
|
|
): selection is SameKindMultiSelectableEditorSelection {
|
|
|
|
|
return (
|
|
|
|
|
selection.kind === "brushes" ||
|
|
|
|
|
selection.kind === "entities" ||
|
|
|
|
|
selection.kind === "modelInstances"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 02:02:34 +02:00
|
|
|
export function cloneEditorSelection(selection: EditorSelection): EditorSelection {
|
|
|
|
|
if (selection.kind === "none") {
|
|
|
|
|
return {
|
|
|
|
|
kind: "none"
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 02:33:18 +02:00
|
|
|
if (selection.kind === "brushFace") {
|
|
|
|
|
return {
|
|
|
|
|
kind: "brushFace",
|
|
|
|
|
brushId: selection.brushId,
|
|
|
|
|
faceId: selection.faceId
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 20:07:17 +02:00
|
|
|
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
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-13 22:24:31 +02:00
|
|
|
if (selection.kind === "pathPoint") {
|
|
|
|
|
return {
|
|
|
|
|
kind: "pathPoint",
|
|
|
|
|
pathId: selection.pathId,
|
|
|
|
|
pointId: selection.pointId
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 22:05:10 +02:00
|
|
|
if (selection.kind === "pathPoints") {
|
|
|
|
|
return {
|
|
|
|
|
kind: "pathPoints",
|
|
|
|
|
pathId: selection.pathId,
|
|
|
|
|
pointIds: [...selection.pointIds]
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 02:02:34 +02:00
|
|
|
return {
|
|
|
|
|
kind: selection.kind,
|
|
|
|
|
ids: [...selection.ids]
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-03 02:09:14 +02:00
|
|
|
export function areEditorSelectionsEqual(left: EditorSelection, right: EditorSelection): boolean {
|
|
|
|
|
if (left.kind !== right.kind) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (left.kind) {
|
|
|
|
|
case "none":
|
|
|
|
|
return true;
|
|
|
|
|
case "brushFace":
|
|
|
|
|
return right.kind === "brushFace" && left.brushId === right.brushId && left.faceId === right.faceId;
|
2026-04-04 20:07:17 +02:00
|
|
|
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;
|
2026-04-13 22:24:31 +02:00
|
|
|
case "pathPoint":
|
|
|
|
|
return right.kind === "pathPoint" && left.pathId === right.pathId && left.pointId === right.pointId;
|
2026-05-12 22:05:10 +02:00
|
|
|
case "pathPoints":
|
|
|
|
|
return right.kind === "pathPoints" && left.pathId === right.pathId && left.pointIds.length === right.pointIds.length && left.pointIds.every((pointId, index) => pointId === right.pointIds[index]);
|
2026-04-03 02:09:14 +02:00
|
|
|
case "brushes":
|
2026-04-18 19:48:07 +02:00
|
|
|
case "terrains":
|
2026-04-13 21:21:40 +02:00
|
|
|
case "paths":
|
2026-04-03 02:09:14 +02:00
|
|
|
case "entities":
|
|
|
|
|
case "modelInstances":
|
|
|
|
|
return right.kind === left.kind && left.ids.length === right.ids.length && left.ids.every((id, index) => id === right.ids[index]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 14:38:55 +02:00
|
|
|
export function getSelectionDefaultActiveId(selection: EditorSelection): string | null {
|
|
|
|
|
switch (selection.kind) {
|
|
|
|
|
case "none":
|
|
|
|
|
return null;
|
|
|
|
|
case "brushFace":
|
|
|
|
|
case "brushEdge":
|
|
|
|
|
case "brushVertex":
|
|
|
|
|
return selection.brushId;
|
|
|
|
|
case "pathPoint":
|
|
|
|
|
return selection.pointId;
|
2026-05-12 22:05:10 +02:00
|
|
|
case "pathPoints":
|
|
|
|
|
return selection.pointIds.at(-1) ?? null;
|
2026-04-15 14:38:55 +02:00
|
|
|
case "brushes":
|
2026-04-18 19:47:48 +02:00
|
|
|
case "terrains":
|
2026-04-15 14:38:55 +02:00
|
|
|
case "paths":
|
|
|
|
|
case "entities":
|
|
|
|
|
case "modelInstances":
|
|
|
|
|
return selection.ids.at(-1) ?? null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function resolveSelectionActiveId(
|
|
|
|
|
selection: EditorSelection,
|
|
|
|
|
activeSelectionId: string | null
|
|
|
|
|
): string | null {
|
|
|
|
|
if (activeSelectionId === null) {
|
|
|
|
|
return getSelectionDefaultActiveId(selection);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (selection.kind) {
|
|
|
|
|
case "none":
|
|
|
|
|
return null;
|
|
|
|
|
case "brushFace":
|
|
|
|
|
case "brushEdge":
|
|
|
|
|
case "brushVertex":
|
|
|
|
|
return selection.brushId === activeSelectionId
|
|
|
|
|
? activeSelectionId
|
|
|
|
|
: selection.brushId;
|
|
|
|
|
case "pathPoint":
|
|
|
|
|
return selection.pointId === activeSelectionId
|
|
|
|
|
? activeSelectionId
|
|
|
|
|
: selection.pointId;
|
2026-05-12 22:05:10 +02:00
|
|
|
case "pathPoints":
|
|
|
|
|
return selection.pointIds.includes(activeSelectionId)
|
|
|
|
|
? activeSelectionId
|
|
|
|
|
: getSelectionDefaultActiveId(selection);
|
2026-04-15 14:38:55 +02:00
|
|
|
case "brushes":
|
2026-04-18 19:47:48 +02:00
|
|
|
case "terrains":
|
2026-04-15 14:38:55 +02:00
|
|
|
case "paths":
|
|
|
|
|
case "entities":
|
|
|
|
|
case "modelInstances":
|
|
|
|
|
return selection.ids.includes(activeSelectionId)
|
|
|
|
|
? activeSelectionId
|
|
|
|
|
: getSelectionDefaultActiveId(selection);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function isSelectionActiveId(
|
|
|
|
|
selection: EditorSelection,
|
|
|
|
|
activeSelectionId: string | null,
|
|
|
|
|
id: string
|
|
|
|
|
): boolean {
|
|
|
|
|
return resolveSelectionActiveId(selection, activeSelectionId) === id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function applySameKindSelectionClick(
|
|
|
|
|
currentSelection: EditorSelection,
|
|
|
|
|
clickedSelection: EditorSelection,
|
|
|
|
|
shiftKey: boolean
|
|
|
|
|
): EditorSelection {
|
2026-05-12 22:05:10 +02:00
|
|
|
if (shiftKey && clickedSelection.kind === "pathPoint") {
|
|
|
|
|
const currentPointIds =
|
|
|
|
|
currentSelection.kind === "pathPoint" &&
|
|
|
|
|
currentSelection.pathId === clickedSelection.pathId
|
|
|
|
|
? [currentSelection.pointId]
|
|
|
|
|
: currentSelection.kind === "pathPoints" &&
|
|
|
|
|
currentSelection.pathId === clickedSelection.pathId
|
|
|
|
|
? currentSelection.pointIds
|
|
|
|
|
: null;
|
|
|
|
|
|
|
|
|
|
if (currentPointIds === null) {
|
|
|
|
|
return cloneEditorSelection(clickedSelection);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const hasPointId = currentPointIds.includes(clickedSelection.pointId);
|
|
|
|
|
const nextPointIds = hasPointId
|
|
|
|
|
? currentPointIds.filter((pointId) => pointId !== clickedSelection.pointId)
|
|
|
|
|
: [...currentPointIds, clickedSelection.pointId];
|
|
|
|
|
|
|
|
|
|
if (nextPointIds.length === 0) {
|
|
|
|
|
return {
|
|
|
|
|
kind: "none"
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nextPointIds.length === 1) {
|
|
|
|
|
return {
|
|
|
|
|
kind: "pathPoint",
|
|
|
|
|
pathId: clickedSelection.pathId,
|
|
|
|
|
pointId: nextPointIds[0]
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
kind: "pathPoints",
|
|
|
|
|
pathId: clickedSelection.pathId,
|
|
|
|
|
pointIds: nextPointIds
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 14:38:55 +02:00
|
|
|
if (!shiftKey || !isSameKindMultiSelectableSelection(clickedSelection)) {
|
|
|
|
|
return cloneEditorSelection(clickedSelection);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
!isSameKindMultiSelectableSelection(currentSelection) ||
|
|
|
|
|
currentSelection.kind !== clickedSelection.kind
|
|
|
|
|
) {
|
|
|
|
|
return cloneEditorSelection(clickedSelection);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (clickedSelection.ids.length !== 1) {
|
|
|
|
|
return cloneEditorSelection(clickedSelection);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const clickedId = clickedSelection.ids[0];
|
|
|
|
|
|
|
|
|
|
if (!currentSelection.ids.includes(clickedId)) {
|
|
|
|
|
return {
|
|
|
|
|
kind: currentSelection.kind,
|
|
|
|
|
ids: [...currentSelection.ids, clickedId]
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const remainingIds = currentSelection.ids.filter((id) => id !== clickedId);
|
|
|
|
|
|
|
|
|
|
return remainingIds.length === 0
|
|
|
|
|
? {
|
|
|
|
|
kind: "none"
|
|
|
|
|
}
|
|
|
|
|
: {
|
|
|
|
|
kind: currentSelection.kind,
|
|
|
|
|
ids: remainingIds
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 15:07:17 +02:00
|
|
|
export function applyEditorSelectionClick(
|
|
|
|
|
currentSelection: EditorSelection,
|
|
|
|
|
clickedSelection: EditorSelection | null,
|
|
|
|
|
shiftKey: boolean
|
|
|
|
|
): EditorSelection {
|
|
|
|
|
if (clickedSelection === null) {
|
|
|
|
|
return shiftKey
|
|
|
|
|
? cloneEditorSelection(currentSelection)
|
|
|
|
|
: {
|
|
|
|
|
kind: "none"
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return applySameKindSelectionClick(
|
|
|
|
|
currentSelection,
|
|
|
|
|
clickedSelection,
|
|
|
|
|
shiftKey
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 02:02:34 +02:00
|
|
|
export function getSingleSelectedBrushId(selection: EditorSelection): string | null {
|
2026-04-04 20:07:17 +02:00
|
|
|
if (selection.kind === "brushFace" || selection.kind === "brushEdge" || selection.kind === "brushVertex") {
|
2026-03-31 02:33:18 +02:00
|
|
|
return selection.brushId;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 02:02:34 +02:00
|
|
|
if (selection.kind !== "brushes" || selection.ids.length !== 1) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return selection.ids[0];
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 07:43:30 +02:00
|
|
|
export function getSelectedBrushFaceId(
|
|
|
|
|
selection: EditorSelection
|
|
|
|
|
): WhiteboxFaceId | null {
|
2026-03-31 02:33:18 +02:00
|
|
|
if (selection.kind !== "brushFace") {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return selection.faceId;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 07:43:30 +02:00
|
|
|
export function getSelectedBrushEdgeId(
|
|
|
|
|
selection: EditorSelection
|
|
|
|
|
): WhiteboxEdgeId | null {
|
2026-04-04 20:07:17 +02:00
|
|
|
if (selection.kind !== "brushEdge") {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return selection.edgeId;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 07:43:30 +02:00
|
|
|
export function getSelectedBrushVertexId(
|
|
|
|
|
selection: EditorSelection
|
|
|
|
|
): WhiteboxVertexId | null {
|
2026-04-04 20:07:17 +02:00
|
|
|
if (selection.kind !== "brushVertex") {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return selection.vertexId;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 03:00:38 +02:00
|
|
|
export function getSingleSelectedEntityId(selection: EditorSelection): string | null {
|
|
|
|
|
if (selection.kind !== "entities" || selection.ids.length !== 1) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return selection.ids[0];
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-13 21:21:40 +02:00
|
|
|
export function getSingleSelectedPathId(selection: EditorSelection): string | null {
|
|
|
|
|
if (selection.kind !== "paths" || selection.ids.length !== 1) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return selection.ids[0];
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-13 22:24:31 +02:00
|
|
|
export function getSingleSelectedPathPoint(selection: EditorSelection): { pathId: string; pointId: string } | null {
|
|
|
|
|
if (selection.kind !== "pathPoint") {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
pathId: selection.pathId,
|
|
|
|
|
pointId: selection.pointId
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getSingleSelectedPathOwnerId(selection: EditorSelection): string | null {
|
|
|
|
|
if (selection.kind === "pathPoint") {
|
|
|
|
|
return selection.pathId;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 22:05:10 +02:00
|
|
|
if (selection.kind === "pathPoints") {
|
|
|
|
|
return selection.pathId;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-13 22:24:31 +02:00
|
|
|
return getSingleSelectedPathId(selection);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 17:36:43 +02:00
|
|
|
export function getSingleSelectedModelInstanceId(selection: EditorSelection): string | null {
|
|
|
|
|
if (selection.kind !== "modelInstances" || selection.ids.length !== 1) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return selection.ids[0];
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-18 19:47:48 +02:00
|
|
|
export function getSingleSelectedTerrainId(selection: EditorSelection): string | null {
|
|
|
|
|
if (selection.kind !== "terrains" || selection.ids.length !== 1) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return selection.ids[0];
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 02:02:34 +02:00
|
|
|
export function isBrushSelected(selection: EditorSelection, brushId: string): boolean {
|
2026-03-31 02:33:18 +02:00
|
|
|
return (
|
|
|
|
|
(selection.kind === "brushes" && selection.ids.includes(brushId)) ||
|
2026-04-04 20:07:17 +02:00
|
|
|
((selection.kind === "brushFace" || selection.kind === "brushEdge" || selection.kind === "brushVertex") &&
|
|
|
|
|
selection.brushId === brushId)
|
2026-03-31 02:33:18 +02:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 07:43:30 +02:00
|
|
|
export function isBrushFaceSelected(
|
|
|
|
|
selection: EditorSelection,
|
|
|
|
|
brushId: string,
|
|
|
|
|
faceId: WhiteboxFaceId
|
|
|
|
|
): boolean {
|
2026-03-31 02:33:18 +02:00
|
|
|
return selection.kind === "brushFace" && selection.brushId === brushId && selection.faceId === faceId;
|
2026-03-31 02:02:34 +02:00
|
|
|
}
|
2026-03-31 17:36:43 +02:00
|
|
|
|
2026-04-15 07:43:30 +02:00
|
|
|
export function isBrushEdgeSelected(
|
|
|
|
|
selection: EditorSelection,
|
|
|
|
|
brushId: string,
|
|
|
|
|
edgeId: WhiteboxEdgeId
|
|
|
|
|
): boolean {
|
2026-04-04 20:07:17 +02:00
|
|
|
return selection.kind === "brushEdge" && selection.brushId === brushId && selection.edgeId === edgeId;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 07:43:30 +02:00
|
|
|
export function isBrushVertexSelected(
|
|
|
|
|
selection: EditorSelection,
|
|
|
|
|
brushId: string,
|
|
|
|
|
vertexId: WhiteboxVertexId
|
|
|
|
|
): boolean {
|
2026-04-04 20:07:17 +02:00
|
|
|
return selection.kind === "brushVertex" && selection.brushId === brushId && selection.vertexId === vertexId;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 17:36:43 +02:00
|
|
|
export function isModelInstanceSelected(selection: EditorSelection, modelInstanceId: string): boolean {
|
|
|
|
|
return selection.kind === "modelInstances" && selection.ids.includes(modelInstanceId);
|
|
|
|
|
}
|
2026-04-04 20:07:17 +02:00
|
|
|
|
2026-04-18 19:47:48 +02:00
|
|
|
export function isTerrainSelected(selection: EditorSelection, terrainId: string): boolean {
|
|
|
|
|
return selection.kind === "terrains" && selection.ids.includes(terrainId);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-13 21:21:40 +02:00
|
|
|
export function isPathSelected(selection: EditorSelection, pathId: string): boolean {
|
2026-04-13 22:24:31 +02:00
|
|
|
return (
|
|
|
|
|
(selection.kind === "paths" && selection.ids.includes(pathId)) ||
|
2026-05-12 22:05:10 +02:00
|
|
|
(selection.kind === "pathPoint" && selection.pathId === pathId) ||
|
|
|
|
|
(selection.kind === "pathPoints" && selection.pathId === pathId)
|
2026-04-13 22:24:31 +02:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function isPathPointSelected(selection: EditorSelection, pathId: string, pointId: string): boolean {
|
2026-05-12 22:05:10 +02:00
|
|
|
return (
|
|
|
|
|
(selection.kind === "pathPoint" &&
|
|
|
|
|
selection.pathId === pathId &&
|
|
|
|
|
selection.pointId === pointId) ||
|
|
|
|
|
(selection.kind === "pathPoints" &&
|
|
|
|
|
selection.pathId === pathId &&
|
|
|
|
|
selection.pointIds.includes(pointId))
|
|
|
|
|
);
|
2026-04-13 21:21:40 +02:00
|
|
|
}
|
|
|
|
|
|
2026-04-04 20:07:17 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|