import type { Asset } from '../model/Asset'; import { type PropertyBag, type PropertyValue, sameValue } from '../model/Properties'; import type { Project } from '../model/Project'; import type { SceneObject } from '../model/SceneObject'; import { propDef, schemaForAsset, schemaForObject } from './PropertySchema'; /** * Schema defaults filled with the asset's own values. Keys unknown to the schema are ignored, so * stale data never leaks into the editor. */ export function resolveAssetProps(project: Project, asset: Asset): PropertyBag { return valuesOver(schemaForAsset(project, asset), asset.props); } /** * The only place where overrides are merged onto asset values. The rows are the object's: a * sprite overriding its shader has that shader's parameters, not the asset's. */ export function resolveSceneObjectProps(project: Project, asset: Asset, object: SceneObject): PropertyBag { const schema = schemaForObject(project, asset, object); const bag = valuesOver(schema, asset.props); for (const def of schema) { const override = object.overrides[def.key]; if (override !== undefined) bag[def.key] = override; } return bag; } function valuesOver( schema: readonly { key: string; default: PropertyValue }[], values: PropertyBag, ): PropertyBag { const bag: PropertyBag = {}; for (const def of schema) { const own = values[def.key]; bag[def.key] = own !== undefined ? own : def.default; } return bag; } /** Whether the asset's own value differs from the schema default – the asset-level override. */ export function isModified(project: Project, asset: Asset, key: string): boolean { const def = propDef(project, asset, key); if (!def) return false; const own = asset.props[key]; return own !== undefined && !sameValue(own, def.default); } /** The value the object would have without its override for `key`. */ export function inheritedValue(project: Project, asset: Asset, key: string): PropertyValue | undefined { return resolveAssetProps(project, asset)[key]; } /** An override that happens to carry the asset's own value does not count as a deviation. */ export function deviatesFromAsset(project: Project, object: SceneObject, asset: Asset, key: string): boolean { const own = object.overrides[key]; return own !== undefined && !sameValue(own, inheritedValue(project, asset, key)); } /** * Compared as overrides, not as effective values: both sides resolve against the same asset, so * "neither overrides" means equal even when the asset value changed since. */ export function deviatesFromTemplate(object: SceneObject, base: SceneObject, key: string): boolean { return !sameValue(object.overrides[key], base.overrides[key]); }