diff --git a/src/core/graph/NodeRegistry.ts b/src/core/graph/NodeRegistry.ts index 1455e7aa..c81656f6 100644 --- a/src/core/graph/NodeRegistry.ts +++ b/src/core/graph/NodeRegistry.ts @@ -113,6 +113,11 @@ export interface NodeDef { * still offers them. Without it a switched-off repeat is two rows of "0" on every key node. */ hiddenParams?(params: PropertyBag): string[]; + /** + * Settings that do nothing at this value set, so neither the node body nor the inspector shows + * them – like the Direction of a linear key. Unlike `hiddenParams`, nothing is left to pick there. + */ + idleParams?(params: PropertyBag): string[]; /** * Which side the ports this node declares for itself (`GraphNode.ports`) appear on. Setting it * is what gives a node the port editor in the inspector. `both`: every port is an output, and an diff --git a/src/core/graph/nodes/action/SetSoundFxParameters.ts b/src/core/graph/nodes/action/SetSoundFxParameters.ts index 61f2d36e..19b5b48f 100644 --- a/src/core/graph/nodes/action/SetSoundFxParameters.ts +++ b/src/core/graph/nodes/action/SetSoundFxParameters.ts @@ -1,6 +1,6 @@ import type { NodeDef } from '../../NodeRegistry'; import { asNumber } from '../../values'; -import { curvesOf, easingOfParams, easingParams, hiddenEasing } from '../easing'; +import { curvesOf, easingOfParams, easingParams, idleEasing } from '../easing'; import { exec, num } from '../shared'; import { fxParamsOf } from './PlaySound'; @@ -30,7 +30,7 @@ export const SetSoundFxParameters: NodeDef = { ], outputs: [exec()], params: easingParams(), - hiddenParams: hiddenEasing, + idleParams: idleEasing, run: (ctx, inputs, params, _def, _entry, lookup) => { const voice = asNumber(inputs.voice); const time = asNumber(inputs.time); diff --git a/src/core/graph/nodes/data/Lerp.ts b/src/core/graph/nodes/data/Lerp.ts index e950c298..6988d479 100644 --- a/src/core/graph/nodes/data/Lerp.ts +++ b/src/core/graph/nodes/data/Lerp.ts @@ -1,6 +1,6 @@ import type { NodeDef } from '../../NodeRegistry'; import { asNumber } from '../../values'; -import { curvesOf, easingOfParams, easingParams, hiddenEasing } from '../easing'; +import { curvesOf, easingOfParams, easingParams, idleEasing } from '../easing'; import { num, withDefault } from '../shared'; import { easedShare } from '../../../model/animation'; @@ -17,7 +17,7 @@ export const Lerp: NodeDef = { inputs: [withDefault(num('a'), 0), withDefault(num('b'), 1), withDefault(num('t'), 0)], outputs: [num('result')], params: easingParams(), - hiddenParams: hiddenEasing, + idleParams: idleEasing, run: (_ctx, inputs, params, _def, _entry, lookup) => { const a = asNumber(inputs.a); const t = Math.min(Math.max(asNumber(inputs.t), 0), 1); diff --git a/src/core/graph/nodes/easing.ts b/src/core/graph/nodes/easing.ts index 4054a421..d2aeec54 100644 --- a/src/core/graph/nodes/easing.ts +++ b/src/core/graph/nodes/easing.ts @@ -39,8 +39,8 @@ export function easingOfParams(params: PropertyBag): KeyInterpolation { return easingValue(shape, direction, textOf(params[LERP_CURVE] ?? '')); } -/** The settings that say nothing for this easing, for `hiddenParams`. */ -export function hiddenEasing(params: PropertyBag): string[] { +/** The settings that do nothing for this easing, for `idleParams` – what a key leaves out, too. */ +export function idleEasing(params: PropertyBag): string[] { const { shape, direction } = easingOf(easingOfParams(params)); return [...(direction ? [] : [DIRECTION]), ...(shape === 'curve' ? [] : [LERP_CURVE])]; } diff --git a/src/editor/graph/NodeView.ts b/src/editor/graph/NodeView.ts index dd066531..37a92ab0 100644 --- a/src/editor/graph/NodeView.ts +++ b/src/editor/graph/NodeView.ts @@ -204,7 +204,10 @@ export class NodeView extends Widget { ): void { this.def = def; this.problem = problem; - const hidden = new Set(this.def.hiddenParams?.(node.params) ?? []); + const hidden = new Set([ + ...(this.def.hiddenParams?.(node.params) ?? []), + ...(this.def.idleParams?.(node.params) ?? []), + ]); const title = this.def.label; const derived = title !== nodeDef(node.type)?.label; this.paramLines = this.def.params diff --git a/src/editor/inspector/nodeSection.ts b/src/editor/inspector/nodeSection.ts index 6e13eb4e..a145db42 100644 --- a/src/editor/inspector/nodeSection.ts +++ b/src/editor/inspector/nodeSection.ts @@ -92,9 +92,11 @@ export function nodeSignature(project: Project, assetId: string, nodeId: string) const choices = variableChoices(project, assetId, node) .map((v) => v.key) .join(','); + // A setting that does nothing has no row, so picking an easing that needs it has to rebuild. + const idle = def?.idleParams?.(node.params).join(',') ?? ''; return `${portSignature(node, asset.graph, lookup, assetId)}|${declared}|${connected.join( ',', - )}|${source}:${choices}|${target}|${transitions}|${soundFxParamsMismatch(node, asset.graph) ?? ''}`; + )}|${source}:${choices}|${target}|${transitions}|${soundFxParamsMismatch(node, asset.graph) ?? ''}|${idle}`; } /** The name of the state at the other end of an edge – what a transition row is labelled with. */ @@ -149,7 +151,10 @@ export function buildNodeSection(host: InspectorHost, assetId: string, nodeId: s buildPortSection(host, assetId, node, variablesOf(asset), typeName); } - const settings = def.params.filter((param) => !(typed && param.key === STRUCT_TYPE_PARAM)); + const idle = new Set(def.idleParams?.(node.params) ?? []); + const settings = def.params.filter( + (param) => !(typed && param.key === STRUCT_TYPE_PARAM) && !idle.has(param.key), + ); if (!ownSettings && settings.length > 0) { addTitle(host, 'SETTINGS'); for (const param of settings) { diff --git a/tests/core/easing-curves.test.ts b/tests/core/easing-curves.test.ts index 12f7f3d6..8fbdc404 100644 --- a/tests/core/easing-curves.test.ts +++ b/tests/core/easing-curves.test.ts @@ -11,6 +11,7 @@ import { } from '../../src/core/commands/animationCommands'; import { CreateAssetCommand } from '../../src/core/commands/assetCommands'; import { CommandError } from '../../src/core/commands/Command'; +import { requireNodeDef } from '../../src/core/commands/require'; import { EASING_DIRECTIONS, EASING_SHAPES, @@ -25,6 +26,7 @@ import { } from '../../src/core/model/animation'; import { previewHolder } from '../../src/core/model/animationPreview'; import type { AnimationTrack, KeyInterpolation } from '../../src/core/model/Asset'; +import { LERP } from '../../src/core/model/curve'; import { findAssetOf } from '../../src/core/model/Project'; import { AnimationSystem } from '../../src/runtime/systems/AnimationSystem'; import { World } from '../../src/runtime/World'; @@ -215,3 +217,20 @@ describe('an overshooting curve and the limits of a property', () => { ).toThrow(CommandError); }); }); + +// Like a key, a node shows Direction and Curve only where they ease something – on its body and in +// the inspector alike. +describe('the easing settings of a node', () => { + for (const type of [LERP, 'SetSoundFxParameters']) { + it(`${type} leaves out what the easing does not use`, () => { + const idle = requireNodeDef(type).idleParams!; + expect(idle({})).toEqual(['direction', 'curve']); + expect(idle({ ease: 'linear', direction: 'in', curve: 'c' })).toEqual(['direction', 'curve']); + expect(idle({ ease: 'step' })).toEqual(['direction', 'curve']); + expect(idle({ ease: 'quad', direction: 'out' })).toEqual(['curve']); + expect(idle({ ease: 'elastic' })).toEqual(['curve']); + expect(idle({ ease: 'curve', direction: 'in' })).toEqual(['direction']); + expect(requireNodeDef(type).hiddenParams).toBeUndefined(); + }); + } +}); diff --git a/tools/e2e/06-graph.mjs b/tools/e2e/06-graph.mjs index 34e4799b..ff556f30 100644 --- a/tools/e2e/06-graph.mjs +++ b/tools/e2e/06-graph.mjs @@ -1174,12 +1174,18 @@ export async function graph(t) { const lerp = (await asset('lerpy')).graph.nodes[0]; const inputs = lerp ? await json(`__gm.graph.nodeInputs(${JSON.stringify(lerp.id)})`) : null; const settings = lerp ? await json(`__gm.graph.nodeSettings(${JSON.stringify(lerp.id)})`) : null; + // Like on a key, linear has no Direction and no Curve to pick, in the inspector either. + const idleFields = [ + await json(`__gm.inspectorField('Direction')`), + await json(`__gm.inspectorField('Curve')`), + ]; check( - 'typing "lerp" into the palette adds a linear Lerp from 0 to 1', + 'typing "lerp" into the palette adds a linear Lerp from 0 to 1, without Direction or Curve', lerp?.type === 'Lerp' && JSON.stringify(inputs) === JSON.stringify(['a 0', 'b 1', 't 0']) && - JSON.stringify(settings) === JSON.stringify(['Easing: Linear']), - JSON.stringify({ lerp, inputs, settings }), + JSON.stringify(settings) === JSON.stringify(['Easing: Linear']) && + idleFields.every((f) => !f), + JSON.stringify({ lerp, inputs, settings, idleFields }), ); const easeField = await json(`__gm.inspectorField('Easing')`); if (easeField) {