Take the cross-cutting queries out of the model's base module
findPropertyUsages walks override sites, so Project.ts imported instance.ts while instance.ts imported Project.ts – the repo's only value-level import cycle. The queries now sit in usages.ts and Project.ts is a base type module again. requireScene has had no caller since holderId replaced sceneId. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b13b130da7
commit
a2f2f8465c
7 files changed
+70
-76
No files matched your search
@@ -1,10 +1,10 @@
|
||||
/** Lookups that throw a `CommandError` when the target does not exist. */
|
||||
import { findNode, type GraphNode } from '../graph/Graph';
|
||||
import { type NodeDef, nodeDef } from '../graph/NodeRegistry';
|
||||
import type { Asset, BehaviorAsset, SceneAsset } from '../model/Asset';
|
||||
import type { Asset, BehaviorAsset } from '../model/Asset';
|
||||
import { findObject, type ObjectTree } from '../model/hierarchy';
|
||||
import { isTemplateContent, objectRef, type ObjectRef } from '../model/instance';
|
||||
import { findAsset, findObjectHolder, findScene, type ObjectHolder, type Project } from '../model/Project';
|
||||
import { findAsset, findObjectHolder, type ObjectHolder, type Project } from '../model/Project';
|
||||
import type { SceneObject } from '../model/SceneObject';
|
||||
import { propDef, type PropertyDef } from '../properties/PropertySchema';
|
||||
import { CommandError } from './Command';
|
||||
@@ -15,12 +15,6 @@ export function requireAsset(project: Project, assetId: string): Asset {
|
||||
return asset;
|
||||
}
|
||||
|
||||
export function requireScene(project: Project, sceneId: string): SceneAsset {
|
||||
const scene = findScene(project, sceneId);
|
||||
if (!scene) throw new CommandError(`Scene ${sceneId} not found`);
|
||||
return scene;
|
||||
}
|
||||
|
||||
export function requireHolder(project: Project, holderId: string): ObjectHolder {
|
||||
const holder = findObjectHolder(project, holderId);
|
||||
if (!holder) throw new CommandError(`Scene or container ${holderId} not found`);
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import { schemaForAsset } from '../properties/PropertySchema';
|
||||
import { type Asset, type ContainerAsset, isSceneAsset, type SceneAsset } from './Asset';
|
||||
import { overrideSites } from './instance';
|
||||
import type { SceneObject } from './SceneObject';
|
||||
|
||||
/**
|
||||
* 2 since a placed container stopped copying its template into the scene. Old files are not
|
||||
@@ -62,55 +59,3 @@ export function findObjectHolder(project: Project, holderId: string): ObjectHold
|
||||
export function listContainers(project: Project): ContainerAsset[] {
|
||||
return project.assets.filter((a): a is ContainerAsset => a.type === 'container');
|
||||
}
|
||||
|
||||
export interface SceneObjectRef {
|
||||
holder: ObjectHolder;
|
||||
object: SceneObject;
|
||||
}
|
||||
|
||||
export function objectsWhere(
|
||||
project: Project,
|
||||
predicate: (object: SceneObject) => boolean,
|
||||
): SceneObjectRef[] {
|
||||
const result: SceneObjectRef[] = [];
|
||||
for (const holder of objectHolders(project)) {
|
||||
for (const object of holder.objects) if (predicate(object)) result.push({ holder, object });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Every placed instance of an asset, in scenes and in container templates alike. */
|
||||
export function findUsages(project: Project, assetId: string): SceneObjectRef[] {
|
||||
return objectsWhere(project, (o) => o.assetId === assetId);
|
||||
}
|
||||
|
||||
export type PropertyUsage =
|
||||
| { on: 'asset'; asset: Asset; key: string }
|
||||
/** `path` is empty for a placed object's own override, set for one a container instance keeps. */
|
||||
| { on: 'object'; holder: ObjectHolder; object: SceneObject; path: string; key: string };
|
||||
|
||||
/**
|
||||
* The other half of "where is this asset used": a texture is never placed, it is worn through a
|
||||
* sprite's `texture` property, so counting uses needs this as well as `findUsages`. Several ids
|
||||
* because every frame cut from a texture is a reference to the same picture (see `textureRefIds`).
|
||||
*/
|
||||
export function findPropertyUsages(project: Project, ids: string | readonly string[]): PropertyUsage[] {
|
||||
const wanted = new Set((typeof ids === 'string' ? [ids] : ids).filter(Boolean));
|
||||
if (wanted.size === 0) return [];
|
||||
const result: PropertyUsage[] = [];
|
||||
const refKeys = new Map<string, string[]>();
|
||||
const matches = (value: unknown): boolean => typeof value === 'string' && wanted.has(value);
|
||||
for (const asset of project.assets) {
|
||||
const keys = schemaForAsset(asset)
|
||||
.filter((def) => def.kind === 'asset' || def.kind === 'texture')
|
||||
.map((def) => def.key);
|
||||
refKeys.set(asset.id, keys);
|
||||
for (const key of keys) if (matches(asset.props[key])) result.push({ on: 'asset', asset, key });
|
||||
}
|
||||
for (const site of overrideSites(project, () => true)) {
|
||||
for (const key of refKeys.get(site.assetId) ?? [])
|
||||
if (matches(site.bag[key]))
|
||||
result.push({ on: 'object', holder: site.holder, object: site.object, path: site.path, key });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -256,8 +256,6 @@ export function instanceTargetOf(
|
||||
};
|
||||
}
|
||||
|
||||
// --- writing -----------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Read/write access to a placed object, whether it is stored in the holder or resolved from a
|
||||
* container template. `object` is the current state either way; the setters keep an instance edit
|
||||
@@ -421,8 +419,6 @@ export function overrideSites(project: Project, wanted: (assetId: string) => boo
|
||||
return sites;
|
||||
}
|
||||
|
||||
// --- unpacking ---------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The resolved content of an instance as objects that can be stored. The derived ids are kept:
|
||||
* they are unique already, and anything pointing at one – the selection, a running game, a check
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* "Where is this asset used" – across scenes and container templates alike. Kept out of
|
||||
* `Project.ts` so that module stays free of the property schema and of container instancing:
|
||||
* both of those already import it, and answering this question needs both.
|
||||
*/
|
||||
import { schemaForAsset } from '../properties/PropertySchema';
|
||||
import type { Asset } from './Asset';
|
||||
import { overrideSites } from './instance';
|
||||
import { type ObjectHolder, objectHolders, type Project } from './Project';
|
||||
import type { SceneObject } from './SceneObject';
|
||||
|
||||
export interface SceneObjectRef {
|
||||
holder: ObjectHolder;
|
||||
object: SceneObject;
|
||||
}
|
||||
|
||||
export function objectsWhere(
|
||||
project: Project,
|
||||
predicate: (object: SceneObject) => boolean,
|
||||
): SceneObjectRef[] {
|
||||
const result: SceneObjectRef[] = [];
|
||||
for (const holder of objectHolders(project)) {
|
||||
for (const object of holder.objects) if (predicate(object)) result.push({ holder, object });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Every placed instance of an asset, in scenes and in container templates alike. */
|
||||
export function findUsages(project: Project, assetId: string): SceneObjectRef[] {
|
||||
return objectsWhere(project, (o) => o.assetId === assetId);
|
||||
}
|
||||
|
||||
export type PropertyUsage =
|
||||
| { on: 'asset'; asset: Asset; key: string }
|
||||
/** `path` is empty for a placed object's own override, set for one a container instance keeps. */
|
||||
| { on: 'object'; holder: ObjectHolder; object: SceneObject; path: string; key: string };
|
||||
|
||||
/**
|
||||
* The other half of "where is this asset used": a texture is never placed, it is worn through a
|
||||
* sprite's `texture` property, so counting uses needs this as well as `findUsages`. Several ids
|
||||
* because every frame cut from a texture is a reference to the same picture (see `textureRefIds`).
|
||||
*/
|
||||
export function findPropertyUsages(project: Project, ids: string | readonly string[]): PropertyUsage[] {
|
||||
const wanted = new Set((typeof ids === 'string' ? [ids] : ids).filter(Boolean));
|
||||
if (wanted.size === 0) return [];
|
||||
const result: PropertyUsage[] = [];
|
||||
const refKeys = new Map<string, string[]>();
|
||||
const matches = (value: unknown): boolean => typeof value === 'string' && wanted.has(value);
|
||||
for (const asset of project.assets) {
|
||||
const keys = schemaForAsset(asset)
|
||||
.filter((def) => def.kind === 'asset' || def.kind === 'texture')
|
||||
.map((def) => def.key);
|
||||
refKeys.set(asset.id, keys);
|
||||
for (const key of keys) if (matches(asset.props[key])) result.push({ on: 'asset', asset, key });
|
||||
}
|
||||
for (const site of overrideSites(project, () => true)) {
|
||||
for (const key of refKeys.get(site.assetId) ?? [])
|
||||
if (matches(site.bag[key]))
|
||||
result.push({ on: 'object', holder: site.holder, object: site.object, path: site.path, key });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -8,8 +8,9 @@ import { CompoundCommand } from '../core/commands/CompoundCommand';
|
||||
import { CreateContainerCommand } from '../core/commands/containerCommands';
|
||||
import { RemoveNodeCommand } from '../core/commands/graphCommands';
|
||||
import { RemoveSceneObjectsCommand } from '../core/commands/sceneObjectCommands';
|
||||
import { findAsset, findPropertyUsages, findUsages } from '../core/model/Project';
|
||||
import { findAsset } from '../core/model/Project';
|
||||
import { textureRefIds } from '../core/model/texture';
|
||||
import { findPropertyUsages, findUsages } from '../core/model/usages';
|
||||
import { Dialog } from '../ui';
|
||||
import { type EditorState, objectSelection } from './state';
|
||||
|
||||
|
||||
Reference in new issue
Block a user