Files
feuerwerk/src/viewport/items.ts
T
rootandClaude Opus 5 9d93020bd8 Add a Container tab and propagate template edits
The Container tab reuses SceneViewport against any object holder, so a
container template is edited exactly like a scene. Right-click no longer
changes the selection, which previously made nesting impossible: opening
the menu on a container loaded it, and "Place in container" then targeted
itself.

Template edits (move, rename, override) now reach already-placed instances
per slot, through a shared templateSync used by both the apply path and the
plain object commands. Copies the user has touched keep their own value.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 20:19:54 +02:00

87 lines
3.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { isHandleEnabled, worldPositionOf } from '../core/graph';
import { type AssetType, isSymbolType } from '../core/model/AssetTypes';
import { editorPosition, treeOrder } from '../core/model/hierarchy';
import type { PropertyBag } from '../core/model/Properties';
import { findAsset, type ObjectHolder, type Project } from '../core/model/Project';
import { resolveTexture, type TextureRef } from '../core/model/texture';
import { enabledAlongChain } from '../core/properties/enabled';
import { resolveSceneObjectProps } from '../core/properties/resolve';
import type { World } from '../runtime/World';
/** What the viewport draws: the same shape whether it comes from the model or the world. */
export interface DrawItem {
id: string;
type: AssetType;
name: string;
/** World position, parents already applied, in the scene view including editor offsets. */
x: number;
y: number;
z: number;
props: PropertyBag;
/** Id of the parent item, empty when top-level. */
parentId: string;
/** Effective switch: this object and all its parents are on (see `properties/enabled.ts`). */
enabled: boolean;
/** Resolved here because drawing has no access to the project. */
texture: TextureRef | null;
}
interface DrawList {
items: DrawItem[];
background: number;
}
/**
* A scene or a container template, in tree order – the tie-break for equal `z` – at the editor
* positions. A container has no background of its own, so it keeps the fallback.
*/
export function collectHolderItems(
project: Project,
holder: ObjectHolder | undefined,
fallbackBackground: number,
): DrawList {
if (!holder) return { items: [], background: fallbackBackground };
const items: DrawItem[] = [];
for (const { object } of treeOrder(holder)) {
const asset = findAsset(project, object.assetId);
if (!asset) continue;
const props = resolveSceneObjectProps(asset, object);
items.push({
id: object.id,
type: asset.type,
name: object.name || asset.name,
...editorPosition(holder, object),
props,
parentId: object.parentId,
enabled: true,
texture: asset.type === 'sprite' ? resolveTexture(project, props) : null,
});
}
const byId = new Map(items.map((i) => [i.id, i]));
for (const item of items)
item.enabled = enabledAlongChain(
item,
(i) => i.props,
(i) => byId.get(i.parentId) ?? null,
);
return { items, background: Number(holder.props.backgroundColor ?? fallbackBackground) };
}
/** The running world: scene content only, since symbols are editor-only. */
export function collectWorldItems(world: World): DrawList {
const items = world.entities
.filter((e) => !isSymbolType(e.type) && isHandleEnabled(e))
.map((e) => ({
id: e.objectId,
type: e.type,
name: e.name,
...worldPositionOf(e),
props: e.props,
parentId: e.parent?.objectId ?? '',
enabled: true,
// Resolved per frame on purpose: a behavior may swap `texture` with `Set property`.
texture: e.type === 'sprite' ? resolveTexture(world.project, e.props) : null,
}));
return { items, background: world.backgroundColor };
}