Add support for transforming path points in the scene document

This commit is contained in:
2026-04-13 22:26:00 +02:00
parent d5b0adb69e
commit 57522886b1

View File

@@ -13,6 +13,7 @@ import {
type BoxFaceId,
type BoxVertexId
} from "../document/brushes";
import { getScenePathPoint } from "../document/paths";
import type { SceneDocument } from "../document/scene-document";
import {
cloneEntityInstance,
@@ -98,6 +99,13 @@ export interface ModelInstanceTransformTarget {
initialScale: Vec3;
}
export interface PathPointTransformTarget {
kind: "pathPoint";
pathId: string;
pointId: string;
initialPosition: Vec3;
}
export interface EntityTransformTarget {
kind: "entity";
entityId: string;
@@ -112,6 +120,7 @@ export type TransformTarget =
| BrushEdgeTransformTarget
| BrushVertexTransformTarget
| ModelInstanceTransformTarget
| PathPointTransformTarget
| EntityTransformTarget;
export interface BrushTransformPreview {
@@ -140,6 +149,11 @@ export interface ModelInstanceTransformPreview {
scale: Vec3;
}
export interface PathPointTransformPreview {
kind: "pathPoint";
position: Vec3;
}
export interface EntityTransformPreview {
kind: "entity";
position: Vec3;
@@ -149,6 +163,7 @@ export interface EntityTransformPreview {
export type TransformPreview =
| BrushTransformPreview
| ModelInstanceTransformPreview
| PathPointTransformPreview
| EntityTransformPreview;
export interface ActiveTransformSession {
@@ -280,6 +295,13 @@ export function cloneTransformTarget(target: TransformTarget): TransformTarget {
initialRotationDegrees: cloneVec3(target.initialRotationDegrees),
initialScale: cloneVec3(target.initialScale)
};
case "pathPoint":
return {
kind: "pathPoint",
pathId: target.pathId,
pointId: target.pointId,
initialPosition: cloneVec3(target.initialPosition)
};
case "entity":
return {
kind: "entity",
@@ -312,6 +334,11 @@ export function cloneTransformPreview(
rotationDegrees: cloneVec3(preview.rotationDegrees),
scale: cloneVec3(preview.scale)
};
case "pathPoint":
return {
kind: "pathPoint",
position: cloneVec3(preview.position)
};
case "entity":
return {
kind: "entity",
@@ -437,6 +464,13 @@ function areTransformTargetsEqual(
) &&
areVec3Equal(left.initialScale, right.initialScale)
);
case "pathPoint":
return (
right.kind === "pathPoint" &&
left.pathId === right.pathId &&
left.pointId === right.pointId &&
areVec3Equal(left.initialPosition, right.initialPosition)
);
case "entity":
return (
right.kind === "entity" &&
@@ -475,6 +509,11 @@ function areTransformPreviewsEqual(
areVec3Equal(left.rotationDegrees, right.rotationDegrees) &&
areVec3Equal(left.scale, right.scale)
);
case "pathPoint":
return (
right.kind === "pathPoint" &&
areVec3Equal(left.position, right.position)
);
case "entity":
return (
right.kind === "entity" &&
@@ -527,6 +566,11 @@ export function createTransformPreviewFromTarget(
rotationDegrees: cloneVec3(target.initialRotationDegrees),
scale: cloneVec3(target.initialScale)
};
case "pathPoint":
return {
kind: "pathPoint",
position: cloneVec3(target.initialPosition)
};
case "entity":
return {
kind: "entity",
@@ -570,6 +614,14 @@ export function doesTransformSessionChangeTarget(
) ||
!areVec3Equal(session.preview.scale, session.target.initialScale))
);
case "pathPoint":
return (
session.preview.kind === "pathPoint" &&
!areVec3Equal(
session.preview.position,
session.target.initialPosition
)
);
case "entity":
return (
session.preview.kind === "entity" &&
@@ -625,6 +677,8 @@ export function getTransformTargetLabel(target: TransformTarget): string {
return `Whitebox Vertex (${BOX_VERTEX_LABELS[target.vertexId]})`;
case "modelInstance":
return getModelInstanceKindLabel();
case "pathPoint":
return "Path Point";
case "entity":
return getEntityKindLabel(target.entityKind);
}
@@ -639,6 +693,7 @@ export function getSupportedTransformOperations(
case "brushEdge":
return ["translate", "rotate", "scale"];
case "brushVertex":
case "pathPoint":
return ["translate"];
case "modelInstance":
return ["translate", "rotate", "scale"];
@@ -667,9 +722,10 @@ export function supportsTransformAxisConstraint(
if (
session.target.kind === "modelInstance" ||
session.target.kind === "brush" ||
session.target.kind === "brushVertex"
session.target.kind === "brushVertex" ||
session.target.kind === "pathPoint"
) {
return session.target.kind !== "brushVertex";
return session.target.kind !== "brushVertex" && session.target.kind !== "pathPoint";
}
if (session.target.kind === "brushFace") {
@@ -757,6 +813,8 @@ export function supportsLocalTransformAxisConstraint(
case "brushEdge":
case "brushVertex":
return session.operation === "translate";
case "pathPoint":
return false;
}
}
@@ -965,6 +1023,40 @@ function createModelInstanceTransformTarget(
};
}
function createPathPointTransformTarget(
document: SceneDocument,
pathId: string,
pointId: string
): TransformTargetResolution {
const path = document.paths[pathId];
if (path === undefined) {
return {
target: null,
message: "Select a path point before transforming it."
};
}
const point = getScenePathPoint(path, pointId);
if (point === null) {
return {
target: null,
message: "Select a valid path point before transforming it."
};
}
return {
target: {
kind: "pathPoint",
pathId,
pointId,
initialPosition: cloneVec3(point.position)
},
message: null
};
}
export function resolveTransformTarget(
document: SceneDocument,
selection: EditorSelection,
@@ -1048,6 +1140,12 @@ export function resolveTransformTarget(
message:
"Path transforms are not available in this slice. Edit path points in the Inspector."
};
case "pathPoint":
return createPathPointTransformTarget(
document,
selection.pathId,
selection.pointId
);
case "modelInstances":
if (selection.ids.length !== 1) {
return {