Fix findUsages/undo/node-lookup bugs, split Viewport and Editor, share command and inspector helpers

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
rootandClaude Opus 5 committed 2026-09-08 09:22:49 +02:00
1 parent 1eb28314cf
commit f140c2fd77
58 files changed
+1969 -1171

No files matched your search

+17 -29
View File
@@ -1,6 +1,7 @@
import { type Removed, removeWhere } from '../util/list';
import type { Position } from './Properties';
import type { SceneObject } from './SceneObject';
import { descendantsBelow, parentChain, sumAlongChain } from './walk';
/**
* Parent/child structure of a scene. `scene.objects` stays a flat list (storage order =
@@ -19,21 +20,18 @@ export function findObject(tree: ObjectTree, id: string): SceneObject | undefine
return tree.objects.find((o) => o.id === id);
}
export function childrenOf(tree: ObjectTree, parentId: string): SceneObject[] {
function childrenOf(tree: ObjectTree, parentId: string): SceneObject[] {
return tree.objects.filter((o) => o.parentId === parentId);
}
/** The parent of an object, or undefined for a top-level one (and for a dangling link). */
function parentOf(tree: ObjectTree, object: SceneObject): SceneObject | undefined {
return object.parentId ? findObject(tree, object.parentId) : undefined;
}
/** All objects below `id`, depth-first, parents before children. */
export function descendantsOf(tree: ObjectTree, id: string): SceneObject[] {
const result: SceneObject[] = [];
const visit = (parentId: string) => {
for (const child of childrenOf(tree, parentId)) {
result.push(child);
visit(child.id);
}
};
visit(id);
return result;
return descendantsBelow(id, (parentId) => childrenOf(tree, parentId), (o) => o.id);
}
/** The ids of `rootIds` and of everything below them – what group operations act on. */
@@ -54,15 +52,7 @@ export function removeSubtrees(tree: ObjectTree, rootIds: readonly string[]): Re
/** An object and every parent above it, nearest first. Stops at a cycle (defensive). */
export function chainOf(tree: ObjectTree, object: SceneObject): SceneObject[] {
const chain = [object];
const seen = new Set<string>([object.id]);
let parent = object.parentId ? findObject(tree, object.parentId) : undefined;
while (parent && !seen.has(parent.id)) {
chain.push(parent);
seen.add(parent.id);
parent = parent.parentId ? findObject(tree, parent.parentId) : undefined;
}
return chain;
return [...parentChain(object, (o) => parentOf(tree, o))];
}
/** Ids of the ancestors of `id`, nearest parent first. Empty for a top-level object. */
@@ -86,19 +76,17 @@ export function subtreeRoots(tree: ObjectTree, ids: readonly string[]): string[]
return ids.filter((id) => !ancestorIdsOf(tree, id).some((ancestorId) => set.has(ancestorId)));
}
function sumAlongChain(tree: ObjectTree, object: SceneObject, withEditorOffset: boolean): Position {
const result: Position = { x: 0, y: 0, z: 0 };
for (const o of chainOf(tree, object)) {
result.x += o.position.x + (withEditorOffset ? o.editorOffset.x : 0);
result.y += o.position.y + (withEditorOffset ? o.editorOffset.y : 0);
result.z += o.position.z;
}
return result;
function positionAlongChain(tree: ObjectTree, object: SceneObject, withEditorOffset: boolean): Position {
return sumAlongChain(
object,
(o) => parentOf(tree, o),
(o) => (withEditorOffset ? { x: o.position.x + o.editorOffset.x, y: o.position.y + o.editorOffset.y, z: o.position.z } : o.position),
);
}
/** Position of an object in world units (all local positions along the chain summed up). */
export function worldPosition(tree: ObjectTree, object: SceneObject): Position {
return sumAlongChain(tree, object, false);
return positionAlongChain(tree, object, false);
}
/**
@@ -107,7 +95,7 @@ export function worldPosition(tree: ObjectTree, object: SceneObject): Position {
* in the game, in collisions or in the graph ever sees it (they all use `worldPosition`).
*/
export function editorPosition(tree: ObjectTree, object: SceneObject): Position {
return sumAlongChain(tree, object, true);
return positionAlongChain(tree, object, true);
}
/** The local position that puts an object at `world` under the given parent ('' = top level). */