Add whitebox selection feedback and box brush components

This commit is contained in:
2026-04-04 20:08:01 +02:00
parent e958c97fdb
commit 8e97fa3485
2 changed files with 135 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
import type { EditorSelection } from "./selection";
import {
BOX_EDGE_LABELS,
BOX_FACE_LABELS,
BOX_VERTEX_LABELS
} from "../document/brushes";
import type { SceneDocument } from "../document/scene-document";
function getBrushDisplayLabel(document: SceneDocument, brushId: string): string {
const brushes = Object.values(document.brushes);
const brushIndex = brushes.findIndex((brush) => brush.id === brushId);
if (brushIndex === -1) {
return "Whitebox Box";
}
return brushes[brushIndex].name ?? `Whitebox Box ${brushIndex + 1}`;
}
export function getWhiteboxSelectionFeedbackLabel(document: SceneDocument, selection: EditorSelection): string | null {
switch (selection.kind) {
case "brushes":
return selection.ids.length === 1 ? `Solid · ${getBrushDisplayLabel(document, selection.ids[0])}` : null;
case "brushFace":
return `Face · ${BOX_FACE_LABELS[selection.faceId]} · ${getBrushDisplayLabel(document, selection.brushId)}`;
case "brushEdge":
return `Edge · ${BOX_EDGE_LABELS[selection.edgeId]} · ${getBrushDisplayLabel(document, selection.brushId)}`;
case "brushVertex":
return `Vertex · ${BOX_VERTEX_LABELS[selection.vertexId]} · ${getBrushDisplayLabel(document, selection.brushId)}`;
default:
return null;
}
}

View File

@@ -0,0 +1,102 @@
import { Euler, MathUtils, Vector3 } from "three";
import type { Vec3 } from "../core/vector";
import {
type BoxBrush,
type BoxEdgeId,
type BoxVertexId
} from "../document/brushes";
import { getBoxBrushHalfSize } from "./box-brush";
type Sign = -1 | 1;
interface BoxVertexSigns {
x: Sign;
y: Sign;
z: Sign;
}
const BOX_VERTEX_SIGNS: Record<BoxVertexId, BoxVertexSigns> = {
negX_negY_negZ: { x: -1, y: -1, z: -1 },
posX_negY_negZ: { x: 1, y: -1, z: -1 },
negX_posY_negZ: { x: -1, y: 1, z: -1 },
posX_posY_negZ: { x: 1, y: 1, z: -1 },
negX_negY_posZ: { x: -1, y: -1, z: 1 },
posX_negY_posZ: { x: 1, y: -1, z: 1 },
negX_posY_posZ: { x: -1, y: 1, z: 1 },
posX_posY_posZ: { x: 1, y: 1, z: 1 }
};
const BOX_EDGE_VERTEX_IDS: Record<BoxEdgeId, { start: BoxVertexId; end: BoxVertexId }> = {
edgeX_negY_negZ: { start: "negX_negY_negZ", end: "posX_negY_negZ" },
edgeX_posY_negZ: { start: "negX_posY_negZ", end: "posX_posY_negZ" },
edgeX_negY_posZ: { start: "negX_negY_posZ", end: "posX_negY_posZ" },
edgeX_posY_posZ: { start: "negX_posY_posZ", end: "posX_posY_posZ" },
edgeY_negX_negZ: { start: "negX_negY_negZ", end: "negX_posY_negZ" },
edgeY_posX_negZ: { start: "posX_negY_negZ", end: "posX_posY_negZ" },
edgeY_negX_posZ: { start: "negX_negY_posZ", end: "negX_posY_posZ" },
edgeY_posX_posZ: { start: "posX_negY_posZ", end: "posX_posY_posZ" },
edgeZ_negX_negY: { start: "negX_negY_negZ", end: "negX_negY_posZ" },
edgeZ_posX_negY: { start: "posX_negY_negZ", end: "posX_negY_posZ" },
edgeZ_negX_posY: { start: "negX_posY_negZ", end: "negX_posY_posZ" },
edgeZ_posX_posY: { start: "posX_posY_negZ", end: "posX_posY_posZ" }
};
export interface BoxBrushEdgeWorldSegment {
id: BoxEdgeId;
start: Vec3;
end: Vec3;
center: Vec3;
}
function createBrushRotationEuler(brush: BoxBrush): Euler {
return new Euler(
MathUtils.degToRad(brush.rotationDegrees.x),
MathUtils.degToRad(brush.rotationDegrees.y),
MathUtils.degToRad(brush.rotationDegrees.z),
"XYZ"
);
}
export function transformBoxBrushLocalPointToWorld(brush: BoxBrush, localPoint: Vec3): Vec3 {
const rotation = createBrushRotationEuler(brush);
const rotatedOffset = new Vector3(localPoint.x, localPoint.y, localPoint.z).applyEuler(rotation);
return {
x: brush.center.x + rotatedOffset.x,
y: brush.center.y + rotatedOffset.y,
z: brush.center.z + rotatedOffset.z
};
}
export function getBoxBrushVertexLocalPosition(brush: BoxBrush, vertexId: BoxVertexId): Vec3 {
const halfSize = getBoxBrushHalfSize(brush);
const signs = BOX_VERTEX_SIGNS[vertexId];
return {
x: signs.x * halfSize.x,
y: signs.y * halfSize.y,
z: signs.z * halfSize.z
};
}
export function getBoxBrushVertexWorldPosition(brush: BoxBrush, vertexId: BoxVertexId): Vec3 {
return transformBoxBrushLocalPointToWorld(brush, getBoxBrushVertexLocalPosition(brush, vertexId));
}
export function getBoxBrushEdgeWorldSegment(brush: BoxBrush, edgeId: BoxEdgeId): BoxBrushEdgeWorldSegment {
const vertexIds = BOX_EDGE_VERTEX_IDS[edgeId];
const start = getBoxBrushVertexWorldPosition(brush, vertexIds.start);
const end = getBoxBrushVertexWorldPosition(brush, vertexIds.end);
return {
id: edgeId,
start,
end,
center: {
x: (start.x + end.x) * 0.5,
y: (start.y + end.y) * 0.5,
z: (start.z + end.z) * 0.5
}
};
}