@vizij/node-graph-authoring 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,2312 +1,86 @@
1
- // src/graphBuilder.ts
2
- import { buildAnimatableValue } from "@vizij/utils";
3
- import { SELF_BINDING_ID as SELF_BINDING_ID2 } from "@vizij/utils";
4
-
5
- // src/state.ts
6
1
  import {
7
- cloneRemapSettings,
8
- SELF_BINDING_ID
9
- } from "@vizij/utils";
10
-
11
- // src/operators.ts
12
- import { requireNodeSignature } from "@vizij/node-graph-wasm/metadata";
13
- var OPERATOR_TYPES = [
14
- "spring",
15
- "damp",
16
- "slew"
17
- ];
18
- function valueJsonToNumber(value) {
19
- if (!value || typeof value !== "object") {
20
- return typeof value === "number" ? value : 0;
21
- }
22
- if ("float" in value && typeof value.float === "number") {
23
- return value.float;
24
- }
25
- if ("int" in value && typeof value.int === "number") {
26
- return value.int;
27
- }
28
- return 0;
29
- }
30
- var operatorDefinitionMap = /* @__PURE__ */ new Map();
31
- OPERATOR_TYPES.forEach((type) => {
32
- const signature = requireNodeSignature(type);
33
- const params = signature.params.map(
34
- (param) => ({
35
- id: param.id,
36
- label: param.label,
37
- doc: param.doc,
38
- min: param.min ?? void 0,
39
- max: param.max ?? void 0,
40
- defaultValue: valueJsonToNumber(param.default_json)
41
- })
42
- );
43
- operatorDefinitionMap.set(type, {
44
- type,
45
- nodeType: signature.type_id,
46
- label: signature.name,
47
- description: signature.doc,
48
- inputs: signature.inputs.map((input) => input.id),
49
- params
50
- });
51
- });
52
- function getBindingOperatorDefinition(type) {
53
- const definition = operatorDefinitionMap.get(type);
54
- if (!definition) {
55
- throw new Error(`Unknown binding operator type '${type}'.`);
56
- }
57
- return definition;
58
- }
59
- var bindingOperatorDefinitions = OPERATOR_TYPES.map((type) => getBindingOperatorDefinition(type));
60
- function createDefaultOperatorSettings(type) {
61
- const definition = getBindingOperatorDefinition(type);
62
- const params = {};
63
- definition.params.forEach((param) => {
64
- params[param.id] = param.defaultValue;
65
- });
66
- return {
67
- type,
68
- enabled: false,
69
- params
70
- };
71
- }
72
- function ensureOperatorParams(operator) {
73
- const definition = getBindingOperatorDefinition(operator.type);
74
- const params = {};
75
- definition.params.forEach((param) => {
76
- const value = operator.params?.[param.id];
77
- params[param.id] = typeof value === "number" ? value : param.defaultValue;
78
- });
79
- return {
80
- type: operator.type,
81
- enabled: !!operator.enabled,
82
- params
83
- };
84
- }
85
- var bindingOperatorTypes = OPERATOR_TYPES;
86
-
87
- // src/state.ts
88
- var VECTOR_ANIMATABLE_TYPES = /* @__PURE__ */ new Set(["vector2", "vector3", "euler", "rgb"]);
89
- function deriveComponentValueType(component) {
90
- if (component.component) {
91
- return "scalar";
92
- }
93
- return VECTOR_ANIMATABLE_TYPES.has(component.animatableType) ? "vector" : "scalar";
94
- }
95
- function isBindingValueType(value) {
96
- return value === "scalar" || value === "vector";
97
- }
98
- function getTargetValueType(target) {
99
- return isBindingValueType(target.valueType) ? target.valueType : "scalar";
100
- }
101
- function sanitizeSlotValueType(value, targetType) {
102
- return isBindingValueType(value) ? value : targetType;
103
- }
104
- function bindingTargetFromComponent(component) {
105
- return {
106
- id: component.id,
107
- defaultValue: component.defaultValue,
108
- range: {
109
- min: component.range.min,
110
- max: component.range.max
111
- },
112
- valueType: deriveComponentValueType(component)
113
- };
114
- }
115
- function bindingTargetFromInput(input) {
116
- return {
117
- id: input.id,
118
- defaultValue: input.defaultValue,
119
- range: {
120
- min: input.range.min,
121
- max: input.range.max
122
- },
123
- valueType: "scalar"
124
- };
125
- }
126
- var DEFAULT_INPUT_RANGE = { min: -1, max: 1 };
127
- var DEFAULT_INPUT_ANCHOR = 0;
128
- var EPSILON = 1e-6;
129
- var LEGACY_SLOT_PATTERN = /^slot_(\d+)$/i;
130
- var ALIAS_SANITIZE_PATTERN = /[^A-Za-z0-9_]+/g;
131
- var PRIMARY_SLOT_ID = "s1";
132
- var PRIMARY_SLOT_ALIAS = "s1";
133
- function createDefaultOperators() {
134
- return bindingOperatorTypes.map(
135
- (type) => createDefaultOperatorSettings(type)
136
- );
137
- }
138
- function normalizeOperator(operator) {
139
- const normalized = ensureOperatorParams(operator);
140
- const definition = getBindingOperatorDefinition(normalized.type);
141
- const params = {};
142
- definition.params.forEach((param) => {
143
- const value = normalized.params[param.id];
144
- params[param.id] = typeof value === "number" ? value : param.defaultValue;
145
- });
146
- return {
147
- type: normalized.type,
148
- enabled: !!normalized.enabled,
149
- params
150
- };
151
- }
152
- function ensureOperators(binding) {
153
- const existing = /* @__PURE__ */ new Map();
154
- (binding.operators ?? []).forEach((operator) => {
155
- try {
156
- existing.set(operator.type, normalizeOperator(operator));
157
- } catch {
158
- }
159
- });
160
- return bindingOperatorTypes.map((type) => {
161
- const current = existing.get(type);
162
- if (current) {
163
- return current;
164
- }
165
- return createDefaultOperatorSettings(type);
166
- });
167
- }
168
- function paramsEqual(a, b) {
169
- const aKeys = Object.keys(a);
170
- const bKeys = Object.keys(b);
171
- if (aKeys.length !== bKeys.length) {
172
- return false;
173
- }
174
- for (const key of aKeys) {
175
- if (a[key] !== b[key]) {
176
- return false;
177
- }
178
- }
179
- return true;
180
- }
181
- function operatorsEqual(a, b) {
182
- if (!a || a.length !== b.length) {
183
- return false;
184
- }
185
- for (let index = 0; index < a.length; index += 1) {
186
- const left = a[index];
187
- const right = b[index];
188
- if (left.type !== right.type || left.enabled !== right.enabled) {
189
- return false;
190
- }
191
- if (!paramsEqual(left.params, right.params)) {
192
- return false;
193
- }
194
- }
195
- return true;
196
- }
197
- function normalizeBindingWithOperators(binding) {
198
- const operators = ensureOperators(binding);
199
- if (operatorsEqual(binding.operators, operators)) {
200
- return { binding, operators };
201
- }
202
- return {
203
- binding: {
204
- ...binding,
205
- operators
206
- },
207
- operators
208
- };
209
- }
210
- function defaultSlotId(index) {
211
- return `s${index + 1}`;
212
- }
213
- function normalizeSlotId(value, index) {
214
- if (value && value.length > 0) {
215
- const match = value.match(LEGACY_SLOT_PATTERN);
216
- if (match) {
217
- const suffix = match[1] ?? String(index + 1);
218
- return `s${suffix}`;
219
- }
220
- return value;
221
- }
222
- return defaultSlotId(index);
223
- }
224
- function normalizeSlotAlias(value, fallback, index) {
225
- if (value && value.length > 0) {
226
- const match = value.match(LEGACY_SLOT_PATTERN);
227
- if (match) {
228
- const suffix = match[1] ?? String(index + 1);
229
- return { alias: `s${suffix}`, replaced: value };
230
- }
231
- return { alias: value, replaced: null };
232
- }
233
- if (fallback && fallback.length > 0) {
234
- return { alias: fallback, replaced: null };
235
- }
236
- return { alias: defaultSlotId(index), replaced: null };
237
- }
238
- function sanitizeAliasInput(raw, fallback, index) {
239
- const trimmed = raw.trim();
240
- if (trimmed.length === 0) {
241
- return fallback || defaultSlotId(index);
242
- }
243
- let sanitized = trimmed.replace(/\s+/g, "_").replace(ALIAS_SANITIZE_PATTERN, "");
244
- if (sanitized.length === 0) {
245
- sanitized = fallback || defaultSlotId(index);
246
- }
247
- if (/^\d/.test(sanitized)) {
248
- sanitized = `s${sanitized}`;
249
- }
250
- return sanitized;
251
- }
252
- function ensureUniqueAlias(candidate, existing) {
253
- if (!existing.has(candidate.toLowerCase())) {
254
- existing.add(candidate.toLowerCase());
255
- return candidate;
256
- }
257
- let suffix = 2;
258
- let next = `${candidate}_${suffix}`;
259
- while (existing.has(next.toLowerCase())) {
260
- suffix += 1;
261
- next = `${candidate}_${suffix}`;
262
- }
263
- existing.add(next.toLowerCase());
264
- return next;
265
- }
266
- function escapeRegExp(value) {
267
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
268
- }
269
- function rewriteLegacyExpression(expression, replacements) {
270
- if (expression.trim().length === 0) {
271
- return expression;
272
- }
273
- return expression.replace(/\bslot_(\d+)\b/gi, (match, digits) => {
274
- const replacement = replacements.get(match);
275
- if (replacement) {
276
- return replacement;
277
- }
278
- return `s${digits}`;
279
- });
280
- }
281
- function clamp(value, min, max) {
282
- return Math.min(max, Math.max(min, value));
283
- }
284
- function isFiniteNumber(value) {
285
- return typeof value === "number" && Number.isFinite(value);
286
- }
287
- function deriveOutputDefaults(target) {
288
- const { min, max } = target.range;
289
- const anchor = clamp(target.defaultValue, min, max);
290
- return {
291
- outLow: min,
292
- outAnchor: anchor,
293
- outHigh: max
294
- };
295
- }
296
- function deriveInputDefaults() {
297
- return {
298
- inLow: DEFAULT_INPUT_RANGE.min,
299
- inAnchor: DEFAULT_INPUT_ANCHOR,
300
- inHigh: DEFAULT_INPUT_RANGE.max
301
- };
302
- }
303
- function migrateLegacyRemap(legacy, target) {
304
- const inputDefaults = deriveInputDefaults();
305
- const outputDefaults = deriveOutputDefaults(target);
306
- const defaults = {
307
- inLow: inputDefaults.inLow,
308
- inAnchor: inputDefaults.inAnchor,
309
- inHigh: inputDefaults.inHigh,
310
- outLow: outputDefaults.outLow,
311
- outAnchor: outputDefaults.outAnchor,
312
- outHigh: outputDefaults.outHigh
313
- };
314
- if ("inLow" in legacy && "inHigh" in legacy && "outLow" in legacy && "outHigh" in legacy) {
315
- const inLow2 = isFiniteNumber(legacy.inLow) ? legacy.inLow : defaults.inLow;
316
- const inAnchor2 = isFiniteNumber(legacy.inAnchor) ? legacy.inAnchor : defaults.inAnchor;
317
- const inHigh2 = isFiniteNumber(legacy.inHigh) ? legacy.inHigh : defaults.inHigh;
318
- let outLow = isFiniteNumber(legacy.outLow) ? legacy.outLow : defaults.outLow;
319
- let outHigh = isFiniteNumber(legacy.outHigh) ? legacy.outHigh : defaults.outHigh;
320
- if (outLow > outHigh) {
321
- const low = outHigh;
322
- const high = outLow;
323
- outLow = low;
324
- outHigh = high;
325
- }
326
- const outAnchor2 = clamp(
327
- isFiniteNumber(legacy.outAnchor) ? legacy.outAnchor : defaults.outAnchor,
328
- outLow,
329
- outHigh
330
- );
331
- return {
332
- inLow: inLow2,
333
- inAnchor: inAnchor2,
334
- inHigh: inHigh2,
335
- outLow,
336
- outAnchor: outAnchor2,
337
- outHigh
338
- };
339
- }
340
- const legacyTyped = legacy;
341
- const inLow = isFiniteNumber(legacyTyped.inMin) ? legacyTyped.inMin : defaults.inLow;
342
- const inHigh = isFiniteNumber(legacyTyped.inMax) ? legacyTyped.inMax : defaults.inHigh;
343
- const inAnchor = (inLow + inHigh) / 2;
344
- const legacyOutMid = isFiniteNumber(legacyTyped.outMin) && isFiniteNumber(legacyTyped.outMax) ? (legacyTyped.outMin + legacyTyped.outMax) / 2 : defaults.outAnchor;
345
- const outAnchor = clamp(legacyOutMid, defaults.outLow, defaults.outHigh);
346
- return {
347
- inLow,
348
- inAnchor,
349
- inHigh,
350
- outLow: defaults.outLow,
351
- outAnchor,
352
- outHigh: defaults.outHigh
353
- };
354
- }
355
- function normalizeRemap(remap, target) {
356
- if (!remap) {
357
- return createDefaultRemap(target);
358
- }
359
- return migrateLegacyRemap(remap, target);
360
- }
361
- function cloneRemap(remap) {
362
- return cloneRemapSettings(remap);
363
- }
364
- function sanitizeRemap(remap, target) {
365
- const normalized = normalizeRemap(remap, target);
366
- const outputDefaults = deriveOutputDefaults(target);
367
- if (!Number.isFinite(normalized.outLow)) {
368
- normalized.outLow = outputDefaults.outLow;
369
- }
370
- if (!Number.isFinite(normalized.outHigh)) {
371
- normalized.outHigh = outputDefaults.outHigh;
372
- }
373
- if (!Number.isFinite(normalized.outAnchor)) {
374
- normalized.outAnchor = outputDefaults.outAnchor;
375
- }
376
- if (normalized.outLow > normalized.outHigh) {
377
- const low = normalized.outHigh;
378
- const high = normalized.outLow;
379
- normalized.outLow = low;
380
- normalized.outHigh = high;
381
- }
382
- normalized.outAnchor = clamp(
383
- normalized.outAnchor,
384
- normalized.outLow,
385
- normalized.outHigh
386
- );
387
- return normalized;
388
- }
389
- function createDefaultRemap(target) {
390
- const inputDefaults = deriveInputDefaults();
391
- const outputDefaults = deriveOutputDefaults(target);
392
- return {
393
- inLow: inputDefaults.inLow,
394
- inAnchor: inputDefaults.inAnchor,
395
- inHigh: inputDefaults.inHigh,
396
- outLow: outputDefaults.outLow,
397
- outAnchor: outputDefaults.outAnchor,
398
- outHigh: outputDefaults.outHigh
399
- };
400
- }
401
- function createDefaultBindings(components) {
402
- const bindings = {};
403
- components.forEach((component) => {
404
- bindings[component.id] = createDefaultBinding(component);
405
- });
406
- return bindings;
407
- }
408
- function createDefaultBinding(component) {
409
- const remap = createDefaultRemap(component);
410
- const valueType = getTargetValueType(component);
411
- return {
412
- targetId: component.id,
413
- inputId: null,
414
- remap,
415
- slots: [
416
- {
417
- id: PRIMARY_SLOT_ID,
418
- alias: PRIMARY_SLOT_ALIAS,
419
- inputId: null,
420
- remap: cloneRemap(remap),
421
- valueType
422
- }
423
- ],
424
- expression: PRIMARY_SLOT_ALIAS,
425
- operators: createDefaultOperators()
426
- };
427
- }
428
- function createDefaultParentBinding(component) {
429
- const base = createDefaultBinding(component);
430
- const ensured = ensurePrimarySlot(base, component);
431
- const slots = ensured.slots.map((slot, index) => {
432
- if (index === 0) {
433
- return {
434
- ...slot,
435
- alias: "self",
436
- inputId: SELF_BINDING_ID
437
- };
438
- }
439
- return slot;
440
- });
441
- return {
442
- ...ensured,
443
- inputId: SELF_BINDING_ID,
444
- slots,
445
- expression: "self"
446
- };
447
- }
448
- function ensurePrimarySlot(binding, target) {
449
- const normalizedBindingRemap = sanitizeRemap(binding.remap, target);
450
- const targetValueType = getTargetValueType(target);
451
- const aliasReplacements = /* @__PURE__ */ new Map();
452
- const sourceSlots = Array.isArray(binding.slots) && binding.slots.length > 0 ? binding.slots : [
453
- {
454
- id: PRIMARY_SLOT_ID,
455
- alias: PRIMARY_SLOT_ALIAS,
456
- inputId: binding.inputId ?? null,
457
- remap: cloneRemap(normalizedBindingRemap),
458
- valueType: targetValueType
459
- }
460
- ];
461
- const normalizedSlots = sourceSlots.map(
462
- (slot, index) => {
463
- const normalizedId = normalizeSlotId(slot.id, index);
464
- const { alias: normalizedAlias, replaced } = normalizeSlotAlias(
465
- slot.alias,
466
- normalizedId,
467
- index
468
- );
469
- if (replaced && replaced !== normalizedAlias) {
470
- aliasReplacements.set(replaced, normalizedAlias);
471
- }
472
- const slotRemapSource = slot.remap ?? (index === 0 ? normalizedBindingRemap : createDefaultRemap(target));
473
- const normalizedSlotRemap = sanitizeRemap(slotRemapSource, target);
474
- const inputId = slot.inputId !== void 0 && slot.inputId !== null ? slot.inputId : index === 0 ? binding.inputId ?? null : null;
475
- const slotValueType = sanitizeSlotValueType(
476
- slot.valueType,
477
- targetValueType
478
- );
479
- return {
480
- id: normalizedId,
481
- alias: normalizedAlias,
482
- inputId,
483
- remap: cloneRemap(normalizedSlotRemap),
484
- valueType: slotValueType
485
- };
486
- }
487
- );
488
- const primary = normalizedSlots[0];
489
- const primaryRemap = sanitizeRemap(primary.remap, target);
490
- const primaryInputId = primary.inputId === SELF_BINDING_ID ? SELF_BINDING_ID : primary.inputId ?? binding.inputId ?? null;
491
- const primaryAlias = primaryInputId === SELF_BINDING_ID ? "self" : primary.alias || PRIMARY_SLOT_ALIAS;
492
- normalizedSlots[0] = {
493
- ...primary,
494
- id: primary.id || PRIMARY_SLOT_ID,
495
- alias: primaryAlias,
496
- inputId: primaryInputId,
497
- remap: cloneRemap(primaryRemap),
498
- valueType: sanitizeSlotValueType(primary.valueType, targetValueType)
499
- };
500
- normalizedSlots.slice(1).forEach((slot, index) => {
501
- const slotRemap = sanitizeRemap(slot.remap, target);
502
- normalizedSlots[index + 1] = {
503
- ...slot,
504
- id: slot.id || defaultSlotId(index + 1),
505
- alias: slot.alias || defaultSlotId(index + 1),
506
- remap: cloneRemap(slotRemap),
507
- valueType: sanitizeSlotValueType(slot.valueType, targetValueType)
508
- };
509
- });
510
- const rawExpression = typeof binding.expression === "string" ? binding.expression.trim() : "";
511
- let expression = rawExpression.length > 0 ? rawExpression : normalizedSlots[0].alias;
512
- expression = rewriteLegacyExpression(expression, aliasReplacements);
513
- const normalizedBinding = {
514
- ...binding,
515
- inputId: normalizedSlots[0].inputId ?? null,
516
- remap: cloneRemap(primaryRemap),
517
- slots: normalizedSlots,
518
- expression
519
- };
520
- const operators = ensureOperators(normalizedBinding);
521
- if (operatorsEqual(normalizedBinding.operators, operators)) {
522
- return normalizedBinding;
523
- }
524
- return {
525
- ...normalizedBinding,
526
- operators
527
- };
528
- }
529
- function createDefaultInputValues(inputs = []) {
530
- const values = {};
531
- inputs.forEach((input) => {
532
- values[input.id] = input.defaultValue;
533
- });
534
- return values;
535
- }
536
- function ensureBindingStructure(binding, target) {
537
- return ensurePrimarySlot(binding, target);
538
- }
539
- function getPrimaryBindingSlot(binding) {
540
- if (!binding.slots || binding.slots.length === 0) {
541
- return null;
542
- }
543
- return binding.slots[0];
544
- }
545
- function addBindingSlot(binding, target) {
546
- const base = ensurePrimarySlot(binding, target);
547
- const nextIndex = base.slots.length + 1;
548
- const slotId = defaultSlotId(nextIndex - 1);
549
- const alias = slotId;
550
- const remap = createDefaultRemap(target);
551
- const nextSlots = [
552
- ...base.slots,
553
- {
554
- id: slotId,
555
- alias,
556
- inputId: null,
557
- remap: cloneRemap(remap)
558
- }
559
- ];
560
- return ensurePrimarySlot(
561
- {
562
- ...base,
563
- slots: nextSlots
564
- },
565
- target
566
- );
567
- }
568
- function removeBindingSlot(binding, target, slotId) {
569
- const base = ensurePrimarySlot(binding, target);
570
- if (base.slots.length <= 1) {
571
- return base;
572
- }
573
- const nextSlots = base.slots.filter((slot) => slot.id !== slotId);
574
- if (nextSlots.length === base.slots.length) {
575
- return base;
576
- }
577
- const nextBinding = ensurePrimarySlot(
578
- {
579
- ...base,
580
- slots: nextSlots
581
- },
582
- target
583
- );
584
- if (!nextBinding.expression) {
585
- return nextBinding;
586
- }
587
- const hasExpressionAlias = nextBinding.slots.some(
588
- (slot) => slot.alias === nextBinding.expression
589
- );
590
- if (!hasExpressionAlias) {
591
- return {
592
- ...nextBinding,
593
- expression: nextBinding.slots[0]?.alias ?? PRIMARY_SLOT_ALIAS
594
- };
595
- }
596
- return nextBinding;
597
- }
598
- function updateBindingSlotAlias(binding, target, slotId, nextAlias) {
599
- const base = ensurePrimarySlot(binding, target);
600
- const slotIndex = base.slots.findIndex((slot) => slot.id === slotId);
601
- if (slotIndex < 0) {
602
- return base;
603
- }
604
- const slots = base.slots.map((slot) => ({ ...slot }));
605
- const currentSlot = slots[slotIndex];
606
- const fallbackAlias = currentSlot.alias || currentSlot.id || defaultSlotId(slotIndex);
607
- const sanitized = sanitizeAliasInput(nextAlias, fallbackAlias, slotIndex);
608
- const existingAliases = /* @__PURE__ */ new Set();
609
- slots.forEach((slot, index) => {
610
- if (index === slotIndex) {
611
- return;
612
- }
613
- if (slot.alias) {
614
- existingAliases.add(slot.alias.toLowerCase());
615
- }
616
- });
617
- const uniqueAlias = ensureUniqueAlias(sanitized, existingAliases);
618
- const previousAlias = currentSlot.alias;
619
- slots[slotIndex] = {
620
- ...currentSlot,
621
- alias: uniqueAlias
622
- };
623
- let nextExpression = base.expression;
624
- if (previousAlias && previousAlias !== uniqueAlias && typeof nextExpression === "string") {
625
- const pattern = new RegExp(`\\b${escapeRegExp(previousAlias)}\\b`, "g");
626
- nextExpression = nextExpression.replace(pattern, uniqueAlias);
627
- }
628
- const updated = ensurePrimarySlot(
629
- {
630
- ...base,
631
- slots,
632
- expression: nextExpression
633
- },
634
- target
635
- );
636
- return updated;
637
- }
638
- function setBindingOperatorEnabled(binding, type, enabled) {
639
- const { binding: normalizedBinding, operators } = normalizeBindingWithOperators(binding);
640
- let changed = false;
641
- const nextOperators = operators.map((operator) => {
642
- if (operator.type !== type) {
643
- return operator;
644
- }
645
- if (operator.enabled === enabled) {
646
- return operator;
647
- }
648
- changed = true;
649
- return {
650
- ...operator,
651
- enabled
652
- };
653
- });
654
- if (!changed) {
655
- return normalizedBinding;
656
- }
657
- if (operatorsEqual(normalizedBinding.operators, nextOperators)) {
658
- return normalizedBinding;
659
- }
660
- return {
661
- ...normalizedBinding,
662
- operators: nextOperators
663
- };
664
- }
665
- function updateBindingOperatorParam(binding, type, paramId, value) {
666
- const { binding: normalizedBinding, operators } = normalizeBindingWithOperators(binding);
667
- const definition = getBindingOperatorDefinition(type);
668
- const paramDefinition = definition.params.find(
669
- (param) => param.id === paramId
670
- );
671
- if (!paramDefinition) {
672
- return normalizedBinding;
673
- }
674
- let nextValue = value;
675
- if (typeof paramDefinition.min === "number") {
676
- nextValue = Math.max(paramDefinition.min, nextValue);
677
- }
678
- if (typeof paramDefinition.max === "number") {
679
- nextValue = Math.min(paramDefinition.max, nextValue);
680
- }
681
- let changed = false;
682
- const nextOperators = operators.map((operator) => {
683
- if (operator.type !== type) {
684
- return operator;
685
- }
686
- if (operator.params[paramId] === nextValue) {
687
- return operator;
688
- }
689
- changed = true;
690
- return {
691
- ...operator,
692
- params: {
693
- ...operator.params,
694
- [paramId]: nextValue
695
- }
696
- };
697
- });
698
- if (!changed) {
699
- return normalizedBinding;
700
- }
701
- if (operatorsEqual(normalizedBinding.operators, nextOperators)) {
702
- return normalizedBinding;
703
- }
704
- return {
705
- ...normalizedBinding,
706
- operators: nextOperators
707
- };
708
- }
709
- function updateBindingExpression(binding, target, expression) {
710
- const base = ensurePrimarySlot(binding, target);
711
- const trimmed = expression.trim();
712
- return {
713
- ...base,
714
- expression: trimmed.length > 0 ? trimmed : base.slots[0]?.alias ?? PRIMARY_SLOT_ALIAS
715
- };
716
- }
717
- function updateBindingSlotRemap(binding, target, slotId, field, value) {
718
- const base = ensurePrimarySlot(binding, target);
719
- const nextSlots = base.slots.map((slot) => {
720
- if (slot.id !== slotId) {
721
- return slot;
722
- }
723
- const updatedRemap = {
724
- ...slot.remap,
725
- [field]: value
726
- };
727
- const sanitized = sanitizeRemap(updatedRemap, target);
728
- return {
729
- ...slot,
730
- remap: cloneRemap(sanitized)
731
- };
732
- });
733
- const updated = ensurePrimarySlot(
734
- {
735
- ...base,
736
- slots: nextSlots
737
- },
738
- target
739
- );
740
- if (updated.slots[0]?.id === slotId) {
741
- updated.remap = {
742
- ...updated.remap,
743
- [field]: value
744
- };
745
- }
746
- return updated;
747
- }
748
- function updateBindingWithInput(binding, target, input, slotId = PRIMARY_SLOT_ID) {
749
- const base = ensurePrimarySlot(binding, target);
750
- const slotIndex = base.slots.findIndex((slot) => slot.id === slotId);
751
- const effectiveIndex = slotIndex >= 0 ? slotIndex : base.slots.length;
752
- const slots = base.slots.map((slot) => ({
753
- ...slot,
754
- remap: cloneRemap(slot.remap)
755
- }));
756
- if (slotIndex === -1) {
757
- const alias = slotId === PRIMARY_SLOT_ID && slots.length === 0 ? PRIMARY_SLOT_ALIAS : slotId;
758
- slots.push({
759
- id: slotId,
760
- alias,
761
- inputId: null,
762
- remap: cloneRemap(createDefaultRemap(target))
763
- });
764
- }
765
- const currentSlot = slots[effectiveIndex];
766
- if (!input) {
767
- const normalizedSlotRemap = sanitizeRemap(currentSlot.remap, target);
768
- const updatedRemap2 = {
769
- ...normalizedSlotRemap,
770
- inLow: DEFAULT_INPUT_RANGE.min,
771
- inAnchor: DEFAULT_INPUT_ANCHOR,
772
- inHigh: DEFAULT_INPUT_RANGE.max
773
- };
774
- slots[effectiveIndex] = {
775
- ...currentSlot,
776
- inputId: null,
777
- remap: cloneRemap(updatedRemap2)
778
- };
779
- if (effectiveIndex === 0) {
780
- return {
781
- ...base,
782
- inputId: null,
783
- remap: cloneRemap(updatedRemap2),
784
- slots
785
- };
786
- }
787
- return {
788
- ...base,
789
- slots
790
- };
791
- }
792
- const normalizedRemap = sanitizeRemap(currentSlot.remap, target);
793
- const updatedRemap = {
794
- ...normalizedRemap,
795
- inLow: input.range.min,
796
- inAnchor: clamp(input.defaultValue, input.range.min, input.range.max),
797
- inHigh: input.range.max,
798
- ...deriveOutputDefaults(target)
799
- };
800
- slots[effectiveIndex] = {
801
- ...currentSlot,
802
- inputId: input.id,
803
- remap: cloneRemap(updatedRemap)
804
- };
805
- if (effectiveIndex === 0) {
806
- return {
807
- ...base,
808
- inputId: input.id,
809
- remap: cloneRemap(updatedRemap),
810
- slots
811
- };
812
- }
813
- return {
814
- ...base,
815
- slots
816
- };
817
- }
818
- function remapValue(value, remap) {
819
- const { inLow, inAnchor, inHigh, outLow, outAnchor, outHigh } = remap;
820
- if (Number.isNaN(value)) {
821
- return outAnchor;
822
- }
823
- if (value <= inAnchor) {
824
- const span2 = inAnchor - inLow;
825
- if (Math.abs(span2) < EPSILON) {
826
- return outLow;
827
- }
828
- const t2 = (value - inLow) / span2;
829
- return outLow + t2 * (outAnchor - outLow);
830
- }
831
- const span = inHigh - inAnchor;
832
- if (Math.abs(span) < EPSILON) {
833
- return outHigh;
834
- }
835
- const t = (value - inAnchor) / span;
836
- return outAnchor + t * (outHigh - outAnchor);
837
- }
838
- function reconcileBindings(previous, components) {
839
- const next = {};
840
- components.forEach((component) => {
841
- const existing = previous[component.id];
842
- if (existing) {
843
- const ensured = ensureBindingStructure(existing, component);
844
- const aliasReplacements = /* @__PURE__ */ new Map();
845
- const slots = ensured.slots.map((slot, index) => {
846
- const normalizedId = normalizeSlotId(slot.id, index);
847
- const { alias: normalizedAlias, replaced } = normalizeSlotAlias(
848
- slot.alias,
849
- normalizedId,
850
- index
851
- );
852
- if (replaced && replaced !== normalizedAlias) {
853
- aliasReplacements.set(replaced, normalizedAlias);
854
- }
855
- const slotRemap = sanitizeRemap(slot.remap, component);
856
- return {
857
- ...slot,
858
- id: normalizedId,
859
- alias: normalizedAlias,
860
- remap: cloneRemap(slotRemap)
861
- };
862
- });
863
- const primary = slots[0];
864
- let expression = typeof ensured.expression === "string" && ensured.expression.trim().length > 0 ? ensured.expression.trim() : primary.alias;
865
- expression = rewriteLegacyExpression(expression, aliasReplacements);
866
- next[component.id] = {
867
- ...ensured,
868
- targetId: component.id,
869
- inputId: primary.inputId ?? null,
870
- remap: cloneRemap(primary.remap),
871
- slots,
872
- expression
873
- };
874
- } else {
875
- next[component.id] = createDefaultBinding(component);
876
- }
877
- });
878
- return next;
879
- }
880
- function bindingToDefinition(binding) {
881
- const operators = binding.operators ? binding.operators.map((operator) => ({
882
- type: operator.type,
883
- enabled: !!operator.enabled,
884
- params: { ...operator.params }
885
- })) : void 0;
886
- const definition = {
887
- inputId: binding.inputId ?? null,
888
- remap: cloneRemap(binding.remap),
889
- slots: binding.slots.map((slot) => ({
890
- ...slot,
891
- remap: cloneRemap(slot.remap)
892
- })),
893
- expression: binding.expression,
894
- operators
895
- };
896
- return definition;
897
- }
898
- function bindingFromDefinition(target, definition) {
899
- if (!definition) {
900
- return createDefaultBinding(target);
901
- }
902
- const definitionOperators = definition.operators;
903
- const binding = {
904
- targetId: target.id,
905
- inputId: definition.inputId ?? null,
906
- remap: cloneRemap(definition.remap),
907
- slots: definition.slots.map((slot) => ({
908
- ...slot,
909
- remap: cloneRemap(slot.remap)
910
- })),
911
- expression: definition.expression,
912
- operators: definitionOperators ? definitionOperators.map((operator) => ({
913
- type: operator.type,
914
- enabled: !!operator.enabled,
915
- params: { ...operator.params }
916
- })) : void 0
917
- };
918
- return ensureBindingStructure(binding, target);
919
- }
920
-
921
- // src/expression.ts
922
- var WHITESPACE = /\s/;
923
- var IDENT_START = /[A-Za-z_]/;
924
- var IDENT_PART = /[A-Za-z0-9_]/;
925
- var DIGIT = /[0-9]/;
926
- var ControlExpressionParser = class {
927
- constructor(input) {
928
- this.input = input;
929
- this.index = 0;
930
- this.errors = [];
931
- }
932
- parse() {
933
- this.skipWhitespace();
934
- const node = this.parseExpression();
935
- this.skipWhitespace();
936
- if (!node) {
937
- if (this.errors.length === 0) {
938
- this.errors.push({
939
- index: this.index,
940
- message: "Empty expression."
941
- });
942
- }
943
- return { node: null, errors: this.errors };
944
- }
945
- if (!this.isAtEnd()) {
946
- this.errors.push({
947
- index: this.index,
948
- message: `Unexpected token "${this.peek()}"`
949
- });
950
- return { node: null, errors: this.errors };
951
- }
952
- return { node, errors: this.errors };
953
- }
954
- parseExpression() {
955
- return this.parseLogicalOr();
956
- }
957
- parseLogicalOr() {
958
- let left = this.parseLogicalAnd();
959
- if (!left) {
960
- return null;
961
- }
962
- while (true) {
963
- this.skipWhitespace();
964
- const operator = this.matchAny(["||"]);
965
- if (!operator) {
966
- break;
967
- }
968
- const right = this.parseLogicalAnd();
969
- if (!right) {
970
- this.errors.push({
971
- index: this.index,
972
- message: "Expected expression after operator."
973
- });
974
- return null;
975
- }
976
- left = {
977
- type: "Binary",
978
- operator,
979
- left,
980
- right
981
- };
982
- }
983
- return left;
984
- }
985
- parseLogicalAnd() {
986
- let left = this.parseComparison();
987
- if (!left) {
988
- return null;
989
- }
990
- while (true) {
991
- this.skipWhitespace();
992
- const operator = this.matchAny(["&&"]);
993
- if (!operator) {
994
- break;
995
- }
996
- const right = this.parseComparison();
997
- if (!right) {
998
- this.errors.push({
999
- index: this.index,
1000
- message: "Expected expression after operator."
1001
- });
1002
- return null;
1003
- }
1004
- left = {
1005
- type: "Binary",
1006
- operator,
1007
- left,
1008
- right
1009
- };
1010
- }
1011
- return left;
1012
- }
1013
- parseComparison() {
1014
- let left = this.parseAdditive();
1015
- if (!left) {
1016
- return null;
1017
- }
1018
- while (true) {
1019
- this.skipWhitespace();
1020
- if (this.input.startsWith(">=", this.index) || this.input.startsWith("<=", this.index)) {
1021
- const op = this.input.slice(this.index, this.index + 2);
1022
- this.index += 2;
1023
- this.errors.push({
1024
- index: this.index - 2,
1025
- message: `Operator "${op}" is not supported.`
1026
- });
1027
- return null;
1028
- }
1029
- const operator = this.matchAny(["==", "!=", ">", "<"]);
1030
- if (!operator) {
1031
- break;
1032
- }
1033
- const right = this.parseAdditive();
1034
- if (!right) {
1035
- this.errors.push({
1036
- index: this.index,
1037
- message: "Expected expression after operator."
1038
- });
1039
- return null;
1040
- }
1041
- left = {
1042
- type: "Binary",
1043
- operator,
1044
- left,
1045
- right
1046
- };
1047
- }
1048
- return left;
1049
- }
1050
- parseAdditive() {
1051
- let left = this.parseMultiplicative();
1052
- if (!left) {
1053
- return null;
1054
- }
1055
- while (true) {
1056
- this.skipWhitespace();
1057
- const operator = this.peek();
1058
- if (operator !== "+" && operator !== "-") {
1059
- break;
1060
- }
1061
- this.index += 1;
1062
- const right = this.parseMultiplicative();
1063
- if (!right) {
1064
- this.errors.push({
1065
- index: this.index,
1066
- message: "Expected expression after operator."
1067
- });
1068
- return null;
1069
- }
1070
- left = {
1071
- type: "Binary",
1072
- operator,
1073
- left,
1074
- right
1075
- };
1076
- }
1077
- return left;
1078
- }
1079
- parseMultiplicative() {
1080
- let left = this.parseUnary();
1081
- if (!left) {
1082
- return null;
1083
- }
1084
- while (true) {
1085
- this.skipWhitespace();
1086
- const operator = this.peek();
1087
- if (operator !== "*" && operator !== "/") {
1088
- break;
1089
- }
1090
- this.index += 1;
1091
- const right = this.parseUnary();
1092
- if (!right) {
1093
- this.errors.push({
1094
- index: this.index,
1095
- message: "Expected expression after operator."
1096
- });
1097
- return null;
1098
- }
1099
- left = {
1100
- type: "Binary",
1101
- operator,
1102
- left,
1103
- right
1104
- };
1105
- }
1106
- return left;
1107
- }
1108
- parseUnary() {
1109
- this.skipWhitespace();
1110
- const char = this.peek();
1111
- if (!char) {
1112
- this.errors.push({
1113
- index: this.index,
1114
- message: "Unexpected end of expression."
1115
- });
1116
- return null;
1117
- }
1118
- if (char === "+" || char === "-" || char === "!") {
1119
- this.index += 1;
1120
- const operand = this.parseUnary();
1121
- if (!operand) {
1122
- this.errors.push({
1123
- index: this.index,
1124
- message: `Expected operand after unary "${char}".`
1125
- });
1126
- return null;
1127
- }
1128
- return {
1129
- type: "Unary",
1130
- operator: char,
1131
- operand
1132
- };
1133
- }
1134
- return this.parsePrimary();
1135
- }
1136
- parsePrimary() {
1137
- this.skipWhitespace();
1138
- const char = this.peek();
1139
- if (!char) {
1140
- this.errors.push({
1141
- index: this.index,
1142
- message: "Unexpected end of expression."
1143
- });
1144
- return null;
1145
- }
1146
- if (char === "(") {
1147
- this.index += 1;
1148
- const expression = this.parseExpression();
1149
- this.skipWhitespace();
1150
- if (this.peek() === ")") {
1151
- this.index += 1;
1152
- return expression;
1153
- }
1154
- this.errors.push({
1155
- index: this.index,
1156
- message: "Unmatched parenthesis."
1157
- });
1158
- return null;
1159
- }
1160
- if (IDENT_START.test(char)) {
1161
- return this.parseIdentifierOrFunction();
1162
- }
1163
- if (DIGIT.test(char) || char === ".") {
1164
- return this.parseNumber();
1165
- }
1166
- this.errors.push({
1167
- index: this.index,
1168
- message: `Unexpected character "${char}".`
1169
- });
1170
- return null;
1171
- }
1172
- parseIdentifierOrFunction() {
1173
- const start = this.index;
1174
- while (!this.isAtEnd() && IDENT_PART.test(this.peek())) {
1175
- this.index += 1;
1176
- }
1177
- const name = this.input.slice(start, this.index);
1178
- if (!name) {
1179
- this.errors.push({
1180
- index: start,
1181
- message: "Invalid identifier."
1182
- });
1183
- return null;
1184
- }
1185
- this.skipWhitespace();
1186
- if (this.peek() === "(") {
1187
- this.index += 1;
1188
- const args = [];
1189
- this.skipWhitespace();
1190
- if (this.peek() === ")") {
1191
- this.index += 1;
1192
- return {
1193
- type: "Function",
1194
- name,
1195
- args
1196
- };
1197
- }
1198
- while (true) {
1199
- const argument = this.parseExpression();
1200
- if (!argument) {
1201
- this.errors.push({
1202
- index: this.index,
1203
- message: `Expected expression for argument ${args.length + 1} of "${name}".`
1204
- });
1205
- return null;
1206
- }
1207
- args.push(argument);
1208
- this.skipWhitespace();
1209
- const next = this.peek();
1210
- if (next === ",") {
1211
- this.index += 1;
1212
- this.skipWhitespace();
1213
- if (this.peek() === ")") {
1214
- this.errors.push({
1215
- index: this.index,
1216
- message: `Expected expression after "," in call to "${name}".`
1217
- });
1218
- return null;
1219
- }
1220
- continue;
1221
- }
1222
- if (next === ")") {
1223
- this.index += 1;
1224
- break;
1225
- }
1226
- if (next === null) {
1227
- this.errors.push({
1228
- index: this.index,
1229
- message: `Unterminated call to "${name}".`
1230
- });
1231
- } else {
1232
- this.errors.push({
1233
- index: this.index,
1234
- message: `Expected "," or ")" in call to "${name}".`
1235
- });
1236
- }
1237
- return null;
1238
- }
1239
- return {
1240
- type: "Function",
1241
- name,
1242
- args
1243
- };
1244
- }
1245
- return {
1246
- type: "Reference",
1247
- name
1248
- };
1249
- }
1250
- parseNumber() {
1251
- const start = this.index;
1252
- let hasDigits = false;
1253
- while (!this.isAtEnd()) {
1254
- const char = this.peek();
1255
- if (DIGIT.test(char)) {
1256
- hasDigits = true;
1257
- this.index += 1;
1258
- continue;
1259
- }
1260
- if (char === ".") {
1261
- this.index += 1;
1262
- continue;
1263
- }
1264
- break;
1265
- }
1266
- const raw = this.input.slice(start, this.index);
1267
- const value = Number(raw);
1268
- if (!hasDigits || Number.isNaN(value)) {
1269
- this.errors.push({
1270
- index: start,
1271
- message: `Invalid numeric literal "${raw}".`
1272
- });
1273
- return null;
1274
- }
1275
- return {
1276
- type: "Literal",
1277
- value
1278
- };
1279
- }
1280
- matchAny(operators) {
1281
- for (const operator of operators) {
1282
- if (this.input.startsWith(operator, this.index)) {
1283
- this.index += operator.length;
1284
- return operator;
1285
- }
1286
- }
1287
- return null;
1288
- }
1289
- skipWhitespace() {
1290
- while (!this.isAtEnd() && WHITESPACE.test(this.peek())) {
1291
- this.index += 1;
1292
- }
1293
- }
1294
- peek() {
1295
- if (this.index >= this.input.length) {
1296
- return null;
1297
- }
1298
- return this.input[this.index] ?? null;
1299
- }
1300
- isAtEnd() {
1301
- return this.index >= this.input.length;
1302
- }
1303
- };
1304
- function parseControlExpression(expression) {
1305
- const parser = new ControlExpressionParser(expression);
1306
- return parser.parse();
1307
- }
1308
- function collectExpressionReferences(node, target = /* @__PURE__ */ new Set()) {
1309
- if (!node) {
1310
- return target;
1311
- }
1312
- switch (node.type) {
1313
- case "Reference":
1314
- target.add(node.name);
1315
- break;
1316
- case "Unary":
1317
- collectExpressionReferences(node.operand, target);
1318
- break;
1319
- case "Binary":
1320
- collectExpressionReferences(node.left, target);
1321
- collectExpressionReferences(node.right, target);
1322
- break;
1323
- case "Function":
1324
- node.args.forEach((arg) => collectExpressionReferences(arg, target));
1325
- break;
1326
- default:
1327
- break;
1328
- }
1329
- return target;
1330
- }
1331
- function mapExpression(node, visit) {
1332
- visit(node);
1333
- switch (node.type) {
1334
- case "Unary":
1335
- mapExpression(node.operand, visit);
1336
- break;
1337
- case "Binary":
1338
- mapExpression(node.left, visit);
1339
- mapExpression(node.right, visit);
1340
- break;
1341
- case "Function":
1342
- node.args.forEach((arg) => mapExpression(arg, visit));
1343
- break;
1344
- default:
1345
- break;
1346
- }
1347
- }
1348
-
1349
- // src/expressionFunctions.ts
1350
- import { requireNodeSignature as requireNodeSignature2 } from "@vizij/node-graph-wasm/metadata";
1351
- var FUNCTION_CONFIGS = [
1352
- { typeId: "sin", names: ["sin"] },
1353
- { typeId: "cos", names: ["cos"] },
1354
- { typeId: "tan", names: ["tan"] },
1355
- { typeId: "power", names: ["power", "pow"] },
1356
- { typeId: "log", names: ["log"] },
1357
- { typeId: "clamp", names: ["clamp"] },
1358
- { typeId: "add", names: ["add"] },
1359
- { typeId: "multiply", names: ["multiply"] },
1360
- { typeId: "subtract", names: ["subtract"] },
1361
- { typeId: "divide", names: ["divide"] },
1362
- {
1363
- typeId: "greaterthan",
1364
- names: ["greaterthan", "gt"],
1365
- minArgs: 2,
1366
- maxArgs: 2
1367
- },
1368
- { typeId: "lessthan", names: ["lessthan", "lt"], minArgs: 2, maxArgs: 2 },
1369
- { typeId: "equal", names: ["equal", "eq"], minArgs: 2, maxArgs: 2 },
1370
- {
1371
- typeId: "notequal",
1372
- names: ["notequal", "neq", "ne"],
1373
- minArgs: 2,
1374
- maxArgs: 2
1375
- },
1376
- { typeId: "and", names: ["and"], minArgs: 2, maxArgs: 2 },
1377
- { typeId: "or", names: ["or"], minArgs: 2, maxArgs: 2 },
1378
- { typeId: "xor", names: ["xor"], minArgs: 2, maxArgs: 2 },
1379
- { typeId: "not", names: ["not"], minArgs: 1, maxArgs: 1 },
1380
- { typeId: "if", names: ["if"], minArgs: 2, maxArgs: 3 },
1381
- { typeId: "time", names: ["time"], minArgs: 0, maxArgs: 0 },
1382
- { typeId: "oscillator", names: ["oscillator"], minArgs: 2, maxArgs: 2 }
1383
- ];
1384
- var SCALAR_FUNCTIONS = /* @__PURE__ */ new Map();
1385
- for (const config of FUNCTION_CONFIGS) {
1386
- const signature = requireNodeSignature2(config.typeId);
1387
- const signatureInputs = signature.inputs;
1388
- const inputs = signatureInputs.map((input) => ({
1389
- id: input.id,
1390
- optional: Boolean(input.optional)
1391
- }));
1392
- const variadic = signature.variadic_inputs ? {
1393
- id: signature.variadic_inputs.id,
1394
- min: signature.variadic_inputs.min,
1395
- max: signature.variadic_inputs.max ?? null
1396
- } : null;
1397
- const derivedMin = variadic ? variadic.min : inputs.filter((input) => !input.optional).length;
1398
- const derivedMax = variadic ? variadic.max : inputs.length;
1399
- const minArgs = config.minArgs ?? derivedMin;
1400
- const maxArgs = config.maxArgs !== void 0 ? config.maxArgs : derivedMax ?? null;
1401
- const definition = {
1402
- nodeType: config.typeId,
1403
- inputs,
1404
- variadic,
1405
- minArgs,
1406
- maxArgs
1407
- };
1408
- const names = new Set(
1409
- [config.typeId, ...config.names].map((name) => name.toLowerCase())
1410
- );
1411
- names.forEach((name) => {
1412
- SCALAR_FUNCTIONS.set(name, definition);
1413
- });
1414
- }
1415
-
1416
- // src/graphBuilder.ts
1417
- function evaluateBinding({
1418
- binding,
1419
- target,
1420
- targetId,
1421
- animatableId,
1422
- component,
1423
- safeId,
1424
- context,
1425
- selfNodeId
1426
- }) {
1427
- const { nodes, edges, ensureInputNode, bindingIssues, summaryBindings } = context;
1428
- const exprContext = {
1429
- componentSafeId: safeId,
1430
- nodes,
1431
- edges,
1432
- constants: /* @__PURE__ */ new Map(),
1433
- counter: 0
1434
- };
1435
- const targetValueType = target.valueType === "vector" ? "vector" : "scalar";
1436
- const aliasNodes = /* @__PURE__ */ new Map();
1437
- const slotSummaries = [];
1438
- const expressionIssues = [];
1439
- const rawExpression = typeof binding.expression === "string" ? binding.expression : "";
1440
- const trimmedExpression = rawExpression.trim();
1441
- let hasActiveSlot = false;
1442
- binding.slots.forEach((slot, index) => {
1443
- const aliasBase = slot.alias?.trim() ?? "";
1444
- const fallbackAlias = `s${index + 1}`;
1445
- const alias = aliasBase.length > 0 ? aliasBase : fallbackAlias;
1446
- const slotId = slot.id && slot.id.length > 0 ? slot.id : alias;
1447
- const slotValueType = slot.valueType === "vector" ? "vector" : "scalar";
1448
- let slotOutputId;
1449
- if (slot.inputId === SELF_BINDING_ID2) {
1450
- if (selfNodeId) {
1451
- slotOutputId = selfNodeId;
1452
- hasActiveSlot = true;
1453
- } else {
1454
- expressionIssues.push("Self reference unavailable for this input.");
1455
- slotOutputId = getConstantNodeId(exprContext, target.defaultValue);
1456
- }
1457
- } else if (slot.inputId) {
1458
- const inputNode = ensureInputNode(slot.inputId);
1459
- if (inputNode) {
1460
- const remapNodeId = `remap_${safeId}_${sanitizeNodeId(slotId)}`;
1461
- nodes.push({
1462
- id: remapNodeId,
1463
- type: "centered_remap",
1464
- input_defaults: {
1465
- in_low: slot.remap.inLow,
1466
- in_anchor: slot.remap.inAnchor,
1467
- in_high: slot.remap.inHigh,
1468
- out_low: slot.remap.outLow,
1469
- out_anchor: slot.remap.outAnchor,
1470
- out_high: slot.remap.outHigh
1471
- }
1472
- });
1473
- edges.push({
1474
- from: { node_id: inputNode.nodeId },
1475
- to: { node_id: remapNodeId, input: "in" }
1476
- });
1477
- slotOutputId = remapNodeId;
1478
- hasActiveSlot = true;
1479
- } else {
1480
- expressionIssues.push(`Missing standard input "${slot.inputId}".`);
1481
- slotOutputId = getConstantNodeId(exprContext, 0);
1482
- }
1483
- } else {
1484
- slotOutputId = getConstantNodeId(exprContext, 0);
1485
- }
1486
- aliasNodes.set(alias, slotOutputId);
1487
- slotSummaries.push({
1488
- targetId,
1489
- animatableId,
1490
- component,
1491
- slotId,
1492
- slotAlias: alias,
1493
- inputId: slot.inputId ?? null,
1494
- remap: { ...slot.remap },
1495
- expression: trimmedExpression,
1496
- valueType: slotValueType
1497
- });
1498
- });
1499
- if (slotSummaries.length === 0) {
1500
- const alias = PRIMARY_SLOT_ALIAS;
1501
- aliasNodes.set(alias, getConstantNodeId(exprContext, 0));
1502
- slotSummaries.push({
1503
- targetId,
1504
- animatableId,
1505
- component,
1506
- slotId: PRIMARY_SLOT_ID,
1507
- slotAlias: alias,
1508
- inputId: null,
1509
- remap: createDefaultRemap(target),
1510
- expression: trimmedExpression,
1511
- valueType: targetValueType
1512
- });
1513
- }
1514
- const defaultAlias = slotSummaries[0]?.slotAlias ?? PRIMARY_SLOT_ALIAS;
1515
- const expressionText = trimmedExpression.length > 0 ? trimmedExpression : defaultAlias;
1516
- const parseResult = parseControlExpression(expressionText);
1517
- let expressionAst = null;
1518
- if (parseResult.node && parseResult.errors.length === 0) {
1519
- const references = collectExpressionReferences(parseResult.node);
1520
- const missing = [];
1521
- references.forEach((ref) => {
1522
- if (!aliasNodes.has(ref)) {
1523
- missing.push(ref);
1524
- }
1525
- });
1526
- if (missing.length === 0) {
1527
- expressionAst = parseResult.node;
1528
- } else {
1529
- missing.forEach((ref) => {
1530
- expressionIssues.push(`Unknown control "${ref}".`);
1531
- });
1532
- }
1533
- } else {
1534
- parseResult.errors.forEach((error) => {
1535
- expressionIssues.push(error.message);
1536
- });
1537
- }
1538
- let valueNodeId = null;
1539
- if (expressionAst) {
1540
- valueNodeId = materializeExpression(
1541
- expressionAst,
1542
- exprContext,
1543
- aliasNodes,
1544
- expressionIssues
1545
- );
1546
- }
1547
- if (!valueNodeId) {
1548
- const fallbackAlias = aliasNodes.has(defaultAlias) ? defaultAlias : aliasNodes.keys().next().value;
1549
- valueNodeId = (fallbackAlias ? aliasNodes.get(fallbackAlias) : void 0) ?? getConstantNodeId(exprContext, 0);
1550
- }
1551
- const issuesCopy = expressionIssues.length ? [...new Set(expressionIssues)] : void 0;
1552
- slotSummaries.forEach((summary) => {
1553
- summary.expression = expressionText;
1554
- if (issuesCopy && issuesCopy.length > 0) {
1555
- summary.issues = issuesCopy;
1556
- const issueSet = bindingIssues.get(summary.targetId) ?? /* @__PURE__ */ new Set();
1557
- issuesCopy.forEach((issue) => issueSet.add(issue));
1558
- bindingIssues.set(summary.targetId, issueSet);
1559
- }
1560
- });
1561
- summaryBindings.push(...slotSummaries);
1562
- return {
1563
- valueNodeId,
1564
- hasActiveSlot
1565
- };
1566
- }
1567
- function sanitizeNodeId(value) {
1568
- return value.replace(/[^a-zA-Z0-9_]/g, "_");
1569
- }
1570
- function buildRigInputPath(faceId, inputPath) {
1571
- const trimmed = inputPath.startsWith("/") ? inputPath.slice(1) : inputPath;
1572
- return `rig/${faceId}/${trimmed}`;
1573
- }
1574
- function getComponentOrder(animatable) {
1575
- switch (animatable.type) {
1576
- case "vector2":
1577
- return ["x", "y"];
1578
- case "vector3":
1579
- case "euler":
1580
- return ["x", "y", "z"];
1581
- case "rgb":
1582
- return ["r", "g", "b"];
1583
- default:
1584
- return null;
1585
- }
1586
- }
1587
- function isComponentRecord(value) {
1588
- return typeof value === "object" && value !== null;
1589
- }
1590
- function componentIndex(component) {
1591
- switch (component) {
1592
- case "x":
1593
- case "r":
1594
- return 0;
1595
- case "y":
1596
- case "g":
1597
- return 1;
1598
- default:
1599
- return 2;
1600
- }
1601
- }
1602
- function extractComponentDefault(value, component) {
1603
- if (typeof value === "number") {
1604
- return value;
1605
- }
1606
- if (Array.isArray(value)) {
1607
- const candidate = value[componentIndex(component)];
1608
- if (typeof candidate === "number" && Number.isFinite(candidate)) {
1609
- return candidate;
1610
- }
1611
- return 0;
1612
- }
1613
- if (value && typeof value === "object") {
1614
- if (!isComponentRecord(value)) {
1615
- return 0;
1616
- }
1617
- const direct = value[component];
1618
- if (typeof direct === "number" && Number.isFinite(direct)) {
1619
- return direct;
1620
- }
1621
- const alt = component === "x" ? value.r : component === "y" ? value.g : value.b;
1622
- if (typeof alt === "number" && Number.isFinite(alt)) {
1623
- return alt;
1624
- }
1625
- }
1626
- return 0;
1627
- }
1628
- function getConstantNodeId(context, value) {
1629
- const key = Number.isFinite(value) ? value.toString() : "NaN";
1630
- const existing = context.constants.get(key);
1631
- if (existing) {
1632
- return existing;
1633
- }
1634
- const nodeId = `const_${context.componentSafeId}_${context.constants.size + 1}`;
1635
- context.nodes.push({
1636
- id: nodeId,
1637
- type: "constant",
1638
- params: {
1639
- value: Number.isFinite(value) ? value : 0
1640
- }
1641
- });
1642
- context.constants.set(key, nodeId);
1643
- return nodeId;
1644
- }
1645
- function createBinaryOperationNode(context, operator, leftId, rightId) {
1646
- const nodeId = `expr_${context.componentSafeId}_${context.counter++}`;
1647
- context.nodes.push({
1648
- id: nodeId,
1649
- type: operator
1650
- });
1651
- const leftInput = operator === "subtract" || operator === "divide" ? "lhs" : "operand_1";
1652
- const rightInput = operator === "subtract" || operator === "divide" ? "rhs" : "operand_2";
1653
- context.edges.push({
1654
- from: { node_id: leftId },
1655
- to: { node_id: nodeId, input: leftInput }
1656
- });
1657
- context.edges.push({
1658
- from: { node_id: rightId },
1659
- to: { node_id: nodeId, input: rightInput }
1660
- });
1661
- return nodeId;
1662
- }
1663
- function createVariadicOperationNode(context, operator, operandIds) {
1664
- if (operandIds.length === 0) {
1665
- return getConstantNodeId(context, operator === "add" ? 0 : 1);
1666
- }
1667
- if (operandIds.length === 1) {
1668
- return operandIds[0];
1669
- }
1670
- const nodeId = `expr_${context.componentSafeId}_${context.counter++}`;
1671
- context.nodes.push({
1672
- id: nodeId,
1673
- type: operator
1674
- });
1675
- operandIds.forEach((operandId, index) => {
1676
- context.edges.push({
1677
- from: { node_id: operandId },
1678
- to: { node_id: nodeId, input: `operand_${index + 1}` }
1679
- });
1680
- });
1681
- return nodeId;
1682
- }
1683
- function createNamedOperationNode(context, operator, inputNames, operandIds) {
1684
- const nodeId = `expr_${context.componentSafeId}_${context.counter++}`;
1685
- context.nodes.push({
1686
- id: nodeId,
1687
- type: operator
1688
- });
1689
- inputNames.forEach((inputName, index) => {
1690
- const operandId = operandIds[index];
1691
- context.edges.push({
1692
- from: { node_id: operandId },
1693
- to: { node_id: nodeId, input: inputName }
1694
- });
1695
- });
1696
- return nodeId;
1697
- }
1698
- function emitScalarFunctionNode(definition, operands, context) {
1699
- if (definition.variadic) {
1700
- const nodeId = `expr_${context.componentSafeId}_${context.counter++}`;
1701
- context.nodes.push({
1702
- id: nodeId,
1703
- type: definition.nodeType
1704
- });
1705
- operands.forEach((operandId, index) => {
1706
- context.edges.push({
1707
- from: { node_id: operandId },
1708
- to: {
1709
- node_id: nodeId,
1710
- input: `${definition.variadic.id}_${index + 1}`
1711
- }
1712
- });
1713
- });
1714
- return nodeId;
1715
- }
1716
- const providedNames = [];
1717
- const providedOperands = [];
1718
- definition.inputs.forEach((input, index) => {
1719
- const operandId = operands[index];
1720
- if (!operandId) {
1721
- return;
1722
- }
1723
- providedNames.push(input.id);
1724
- providedOperands.push(operandId);
1725
- });
1726
- return createNamedOperationNode(
1727
- context,
1728
- definition.nodeType,
1729
- providedNames,
1730
- providedOperands
1731
- );
1732
- }
1733
- function applyBindingOperators(operators, baseNodeId, safeId, nodes, edges) {
1734
- let currentNodeId = baseNodeId;
1735
- operators.forEach((operator, index) => {
1736
- if (!operator.enabled) {
1737
- return;
1738
- }
1739
- let definition;
1740
- try {
1741
- definition = getBindingOperatorDefinition(operator.type);
1742
- } catch {
1743
- return;
1744
- }
1745
- const nodeId = `${operator.type}_${safeId}_${index + 1}`;
1746
- const params = {};
1747
- definition.params.forEach((param) => {
1748
- const configured = operator.params?.[param.id];
1749
- params[param.id] = typeof configured === "number" ? configured : param.defaultValue;
1750
- });
1751
- nodes.push({
1752
- id: nodeId,
1753
- type: definition.nodeType,
1754
- params
1755
- });
1756
- const inputId = definition.inputs[0] ?? "in";
1757
- edges.push({
1758
- from: { node_id: currentNodeId },
1759
- to: { node_id: nodeId, input: inputId }
1760
- });
1761
- currentNodeId = nodeId;
1762
- });
1763
- return currentNodeId;
1764
- }
1765
- function collectOperands(node, operator, target) {
1766
- if (node.type === "Binary" && node.operator === operator) {
1767
- collectOperands(node.left, operator, target);
1768
- collectOperands(node.right, operator, target);
1769
- return;
1770
- }
1771
- target.push(node);
1772
- }
1773
- var BINARY_FUNCTION_OPERATOR_MAP = {
1774
- ">": "greaterthan",
1775
- "<": "lessthan",
1776
- "==": "equal",
1777
- "!=": "notequal",
1778
- "&&": "and",
1779
- "||": "or"
1780
- };
1781
- function materializeExpression(node, context, aliasNodes, issues) {
1782
- switch (node.type) {
1783
- case "Literal": {
1784
- return getConstantNodeId(context, node.value);
1785
- }
1786
- case "Reference": {
1787
- const mapped = aliasNodes.get(node.name);
1788
- if (!mapped) {
1789
- issues.push(`Unknown control "${node.name}".`);
1790
- return getConstantNodeId(context, 0);
1791
- }
1792
- return mapped;
1793
- }
1794
- case "Unary": {
1795
- const operandId = materializeExpression(
1796
- node.operand,
1797
- context,
1798
- aliasNodes,
1799
- issues
1800
- );
1801
- switch (node.operator) {
1802
- case "+":
1803
- return operandId;
1804
- case "-": {
1805
- const negativeOne = getConstantNodeId(context, -1);
1806
- return createVariadicOperationNode(context, "multiply", [
1807
- negativeOne,
1808
- operandId
1809
- ]);
1810
- }
1811
- case "!": {
1812
- const definition = SCALAR_FUNCTIONS.get("not");
1813
- if (!definition) {
1814
- issues.push('Function "not" is not available in metadata.');
1815
- return getConstantNodeId(context, 0);
1816
- }
1817
- return emitScalarFunctionNode(definition, [operandId], context);
1818
- }
1819
- default:
1820
- issues.push("Unsupported unary operator.");
1821
- return operandId;
1822
- }
1823
- }
1824
- case "Binary": {
1825
- const operator = node.operator;
1826
- if (operator === "+") {
1827
- const children = [];
1828
- collectOperands(node, "+", children);
1829
- const operandIds = children.map(
1830
- (child) => materializeExpression(child, context, aliasNodes, issues)
1831
- );
1832
- return createVariadicOperationNode(context, "add", operandIds);
1833
- }
1834
- if (operator === "*") {
1835
- const children = [];
1836
- collectOperands(node, "*", children);
1837
- const operandIds = children.map(
1838
- (child) => materializeExpression(child, context, aliasNodes, issues)
1839
- );
1840
- return createVariadicOperationNode(context, "multiply", operandIds);
1841
- }
1842
- const leftId = materializeExpression(
1843
- node.left,
1844
- context,
1845
- aliasNodes,
1846
- issues
1847
- );
1848
- const rightId = materializeExpression(
1849
- node.right,
1850
- context,
1851
- aliasNodes,
1852
- issues
1853
- );
1854
- if (operator === "-") {
1855
- return createBinaryOperationNode(context, "subtract", leftId, rightId);
1856
- }
1857
- if (operator === "/") {
1858
- return createBinaryOperationNode(context, "divide", leftId, rightId);
1859
- }
1860
- const mappedFunction = BINARY_FUNCTION_OPERATOR_MAP[operator];
1861
- if (mappedFunction) {
1862
- const definition = SCALAR_FUNCTIONS.get(mappedFunction);
1863
- if (!definition) {
1864
- issues.push(`Function "${mappedFunction}" is not available.`);
1865
- return getConstantNodeId(context, 0);
1866
- }
1867
- return emitScalarFunctionNode(definition, [leftId, rightId], context);
1868
- }
1869
- issues.push(`Unsupported operator "${operator}".`);
1870
- return getConstantNodeId(context, 0);
1871
- }
1872
- case "Function": {
1873
- const name = node.name;
1874
- const normalized = name.toLowerCase();
1875
- const definition = SCALAR_FUNCTIONS.get(normalized);
1876
- if (!definition) {
1877
- issues.push(`Unknown function "${name}".`);
1878
- return getConstantNodeId(context, 0);
1879
- }
1880
- const operands = node.args.map(
1881
- (arg) => materializeExpression(arg, context, aliasNodes, issues)
1882
- );
1883
- if (operands.length < definition.minArgs) {
1884
- issues.push(
1885
- `Function "${name}" expects at least ${definition.minArgs} arguments, received ${operands.length}.`
1886
- );
1887
- return getConstantNodeId(context, 0);
1888
- }
1889
- if (definition.maxArgs !== null && operands.length > definition.maxArgs) {
1890
- issues.push(
1891
- `Function "${name}" expects at most ${definition.maxArgs} arguments, received ${operands.length}.`
1892
- );
1893
- return getConstantNodeId(context, 0);
1894
- }
1895
- return emitScalarFunctionNode(definition, operands, context);
1896
- }
1897
- default: {
1898
- issues.push("Unsupported expression node.");
1899
- return getConstantNodeId(context, 0);
1900
- }
1901
- }
1902
- }
1903
- function buildRigGraphSpec({
1904
- faceId,
1905
- animatables,
1906
- components,
1907
- bindings,
1908
- inputsById,
1909
- inputBindings
1910
- }) {
1911
- const nodes = [];
1912
- const edges = [];
1913
- const inputNodes = /* @__PURE__ */ new Map();
1914
- const buildingDerived = /* @__PURE__ */ new Set();
1915
- const computedInputs = /* @__PURE__ */ new Set();
1916
- const summaryBindings = [];
1917
- const bindingIssues = /* @__PURE__ */ new Map();
1918
- const animatableEntries = /* @__PURE__ */ new Map();
1919
- const outputs = /* @__PURE__ */ new Set();
1920
- const ensureInputNode = (inputId) => {
1921
- const existing = inputNodes.get(inputId);
1922
- if (existing) {
1923
- return existing;
1924
- }
1925
- const input = inputsById.get(inputId);
1926
- if (!input) {
1927
- return null;
1928
- }
1929
- const defaultValue = Number.isFinite(input.defaultValue) ? input.defaultValue : 0;
1930
- const inputBindingRaw = inputBindings[inputId];
1931
- if (inputBindingRaw) {
1932
- if (buildingDerived.has(inputId)) {
1933
- const issueSet = bindingIssues.get(inputId) ?? /* @__PURE__ */ new Set();
1934
- issueSet.add("Derived input cycle detected.");
1935
- bindingIssues.set(inputId, issueSet);
1936
- return null;
1937
- }
1938
- buildingDerived.add(inputId);
1939
- try {
1940
- const target = bindingTargetFromInput(input);
1941
- const binding = ensureBindingStructure(inputBindingRaw, target);
1942
- const requiresSelf = binding.inputId === SELF_BINDING_ID2 || binding.slots.some((slot) => slot.inputId === SELF_BINDING_ID2);
1943
- let selfNodeId;
1944
- if (requiresSelf) {
1945
- const sliderNodeId = `input_raw_${sanitizeNodeId(inputId)}`;
1946
- nodes.push({
1947
- id: sliderNodeId,
1948
- type: "input",
1949
- params: {
1950
- path: buildRigInputPath(faceId, input.path),
1951
- value: { float: defaultValue }
1952
- }
1953
- });
1954
- selfNodeId = sliderNodeId;
1955
- }
1956
- const { valueNodeId, hasActiveSlot } = evaluateBinding({
1957
- binding,
1958
- target,
1959
- targetId: inputId,
1960
- animatableId: inputId,
1961
- component: void 0,
1962
- safeId: sanitizeNodeId(inputId),
1963
- context: {
1964
- nodes,
1965
- edges,
1966
- ensureInputNode,
1967
- bindingIssues,
1968
- summaryBindings
1969
- },
1970
- selfNodeId
1971
- });
1972
- if (!valueNodeId || !hasActiveSlot) {
1973
- const constNodeId = `derived_default_${sanitizeNodeId(inputId)}`;
1974
- nodes.push({
1975
- id: constNodeId,
1976
- type: "constant",
1977
- params: {
1978
- value: input.defaultValue
1979
- }
1980
- });
1981
- const record3 = { nodeId: constNodeId, input };
1982
- inputNodes.set(inputId, record3);
1983
- return record3;
1984
- }
1985
- computedInputs.add(inputId);
1986
- const record2 = { nodeId: valueNodeId, input };
1987
- inputNodes.set(inputId, record2);
1988
- return record2;
1989
- } finally {
1990
- buildingDerived.delete(inputId);
1991
- }
1992
- }
1993
- const nodeId = `input_${sanitizeNodeId(inputId)}`;
1994
- nodes.push({
1995
- id: nodeId,
1996
- type: "input",
1997
- params: {
1998
- path: buildRigInputPath(faceId, input.path),
1999
- value: { float: defaultValue }
2000
- }
2001
- });
2002
- const record = { nodeId, input };
2003
- inputNodes.set(inputId, record);
2004
- return record;
2005
- };
2006
- const ensureAnimatableEntry = (animatableId) => {
2007
- const existing = animatableEntries.get(animatableId);
2008
- if (existing) {
2009
- return existing;
2010
- }
2011
- const animatable = animatables[animatableId];
2012
- if (!animatable) {
2013
- return null;
2014
- }
2015
- const entry = {
2016
- animatable,
2017
- values: /* @__PURE__ */ new Map(),
2018
- defaults: /* @__PURE__ */ new Map(),
2019
- isDriven: false
2020
- };
2021
- animatableEntries.set(animatableId, entry);
2022
- return entry;
2023
- };
2024
- components.forEach((component) => {
2025
- const bindingRaw = bindings[component.id];
2026
- const target = bindingTargetFromComponent(component);
2027
- const binding = bindingRaw ? ensureBindingStructure(bindingRaw, target) : null;
2028
- const entry = ensureAnimatableEntry(component.animatableId);
2029
- if (!entry) {
2030
- return;
2031
- }
2032
- const key = component.component ?? "scalar";
2033
- let valueNodeId = null;
2034
- let hasActiveSlot = false;
2035
- if (binding) {
2036
- const { valueNodeId: producedNodeId, hasActiveSlot: active } = evaluateBinding({
2037
- binding,
2038
- target,
2039
- targetId: component.id,
2040
- animatableId: component.animatableId,
2041
- component: component.component,
2042
- safeId: component.safeId,
2043
- context: {
2044
- nodes,
2045
- edges,
2046
- ensureInputNode,
2047
- bindingIssues,
2048
- summaryBindings
2049
- }
2050
- });
2051
- valueNodeId = producedNodeId;
2052
- hasActiveSlot = active;
2053
- if (active) {
2054
- entry.isDriven = true;
2055
- }
2056
- } else {
2057
- summaryBindings.push({
2058
- targetId: component.id,
2059
- animatableId: component.animatableId,
2060
- component: component.component,
2061
- slotId: PRIMARY_SLOT_ID,
2062
- slotAlias: PRIMARY_SLOT_ALIAS,
2063
- inputId: null,
2064
- remap: createDefaultRemap(target),
2065
- expression: PRIMARY_SLOT_ALIAS,
2066
- valueType: target.valueType === "vector" ? "vector" : "scalar",
2067
- issues: ["Binding not found."]
2068
- });
2069
- const fallbackIssues = bindingIssues.get(component.id) ?? /* @__PURE__ */ new Set();
2070
- fallbackIssues.add("Binding not found.");
2071
- bindingIssues.set(component.id, fallbackIssues);
2072
- }
2073
- if (!valueNodeId || !hasActiveSlot) {
2074
- entry.defaults.set(key, component.defaultValue);
2075
- return;
2076
- }
2077
- const operatorList = binding ? binding.operators ?? [] : [];
2078
- const finalNodeId = applyBindingOperators(
2079
- operatorList,
2080
- valueNodeId,
2081
- component.safeId,
2082
- nodes,
2083
- edges
2084
- );
2085
- entry.values.set(key, finalNodeId);
2086
- });
2087
- inputsById.forEach((_input, inputId) => {
2088
- ensureInputNode(inputId);
2089
- });
2090
- animatableEntries.forEach((entry, animatableId) => {
2091
- if (!entry.isDriven) {
2092
- return;
2093
- }
2094
- outputs.add(animatableId);
2095
- const safeId = sanitizeNodeId(animatableId);
2096
- const order = getComponentOrder(entry.animatable);
2097
- if (!order) {
2098
- const valueNodeId = entry.values.get("scalar");
2099
- if (!valueNodeId) {
2100
- return;
2101
- }
2102
- const outputNodeId2 = `out_${safeId}`;
2103
- nodes.push({
2104
- id: outputNodeId2,
2105
- type: "output",
2106
- params: {
2107
- path: animatableId
2108
- }
2109
- });
2110
- edges.push({
2111
- from: { node_id: valueNodeId },
2112
- to: { node_id: outputNodeId2, input: "in" }
2113
- });
2114
- return;
2115
- }
2116
- const joinNodeId = `join_${safeId}`;
2117
- nodes.push({
2118
- id: joinNodeId,
2119
- type: "join"
2120
- });
2121
- order.forEach((componentKey, index) => {
2122
- let sourceId = entry.values.get(componentKey);
2123
- if (!sourceId) {
2124
- const componentDefault = entry.defaults.get(componentKey) ?? extractComponentDefault(
2125
- buildAnimatableValue(entry.animatable, void 0),
2126
- componentKey
2127
- );
2128
- const constNodeId = `const_${safeId}_${componentKey}`;
2129
- nodes.push({
2130
- id: constNodeId,
2131
- type: "constant",
2132
- params: {
2133
- value: componentDefault
2134
- }
2135
- });
2136
- sourceId = constNodeId;
2137
- }
2138
- edges.push({
2139
- from: { node_id: sourceId },
2140
- to: { node_id: joinNodeId, input: `operand_${index + 1}` }
2141
- });
2142
- });
2143
- const outputNodeId = `out_${safeId}`;
2144
- nodes.push({
2145
- id: outputNodeId,
2146
- type: "output",
2147
- params: {
2148
- path: animatableId
2149
- }
2150
- });
2151
- edges.push({
2152
- from: { node_id: joinNodeId },
2153
- to: { node_id: outputNodeId, input: "in" }
2154
- });
2155
- });
2156
- const nodeById = /* @__PURE__ */ new Map();
2157
- nodes.forEach((node) => {
2158
- nodeById.set(node.id, node);
2159
- });
2160
- const constantUsage = /* @__PURE__ */ new Map();
2161
- edges.forEach((edge) => {
2162
- const source = nodeById.get(edge.from.node_id);
2163
- if (source?.type === "constant") {
2164
- constantUsage.set(source.id, (constantUsage.get(source.id) ?? 0) + 1);
2165
- }
2166
- });
2167
- const updatedEdges = [];
2168
- const constantsToRemove = /* @__PURE__ */ new Set();
2169
- edges.forEach((edge) => {
2170
- const source = nodeById.get(edge.from.node_id);
2171
- if (source?.type === "constant" && constantUsage.get(source.id) === 1 && source.params && Object.prototype.hasOwnProperty.call(source.params, "value")) {
2172
- const target = nodeById.get(edge.to.node_id);
2173
- if (target) {
2174
- const value = source.params.value;
2175
- if (value !== void 0) {
2176
- target.input_defaults = {
2177
- ...target.input_defaults ?? {},
2178
- [edge.to.input]: value
2179
- };
2180
- nodeById.set(target.id, target);
2181
- constantsToRemove.add(source.id);
2182
- return;
2183
- }
2184
- }
2185
- }
2186
- updatedEdges.push(edge);
2187
- });
2188
- const filteredNodes = nodes.filter((node) => !constantsToRemove.has(node.id)).map((node) => nodeById.get(node.id) ?? node);
2189
- const remapDefaultIssues = validateRemapDefaults(filteredNodes);
2190
- const dynamicOutputs = Array.from(outputs);
2191
- const computedInputList = Array.from(computedInputs);
2192
- const filteredSummaryBindings = summaryBindings.filter(
2193
- (binding) => outputs.has(binding.animatableId) || computedInputs.has(binding.animatableId)
2194
- );
2195
- const spec = {
2196
- nodes: filteredNodes,
2197
- edges: updatedEdges.length ? updatedEdges : void 0
2198
- };
2199
- const baseSpec = spec;
2200
- const specWithMetadata = {
2201
- ...baseSpec,
2202
- metadata: {
2203
- ...baseSpec.metadata,
2204
- vizij: {
2205
- faceId,
2206
- inputs: Array.from(inputsById.values()).map((input) => ({
2207
- id: input.id,
2208
- path: input.path,
2209
- sourceId: input.sourceId,
2210
- label: input.label,
2211
- group: input.group,
2212
- defaultValue: input.defaultValue,
2213
- range: {
2214
- min: input.range.min,
2215
- max: input.range.max
2216
- }
2217
- })),
2218
- bindings: filteredSummaryBindings.map((binding) => ({
2219
- ...binding,
2220
- remap: { ...binding.remap },
2221
- expression: binding.expression,
2222
- valueType: binding.valueType,
2223
- issues: binding.issues ? [...binding.issues] : void 0
2224
- }))
2225
- }
2226
- }
2227
- };
2228
- const issuesByTarget = {};
2229
- bindingIssues.forEach((issues, targetId) => {
2230
- if (issues.size === 0) {
2231
- return;
2232
- }
2233
- issuesByTarget[targetId] = Array.from(issues);
2234
- });
2235
- const fatalIssues = /* @__PURE__ */ new Set();
2236
- Object.values(issuesByTarget).forEach((issues) => {
2237
- issues.forEach((issue) => fatalIssues.add(issue));
2238
- });
2239
- remapDefaultIssues.forEach((issue) => fatalIssues.add(issue));
2240
- return {
2241
- spec: specWithMetadata,
2242
- summary: {
2243
- faceId,
2244
- inputs: Array.from(inputNodes.values()).map(
2245
- ({ input }) => buildRigInputPath(faceId, input.path)
2246
- ),
2247
- outputs: [...dynamicOutputs, ...computedInputList],
2248
- bindings: filteredSummaryBindings
2249
- },
2250
- issues: {
2251
- byTarget: issuesByTarget,
2252
- fatal: Array.from(fatalIssues)
2253
- }
2254
- };
2255
- }
2256
- function validateRemapDefaults(nodes) {
2257
- const issues = [];
2258
- nodes.forEach((node) => {
2259
- if (node.type !== "centered_remap") {
2260
- return;
2261
- }
2262
- const defaults = node.input_defaults ?? {};
2263
- [
2264
- "in_low",
2265
- "in_anchor",
2266
- "in_high",
2267
- "out_low",
2268
- "out_anchor",
2269
- "out_high"
2270
- ].forEach((key) => {
2271
- const value = defaults[key];
2272
- if (typeof value !== "number" || !Number.isFinite(value)) {
2273
- issues.push(`Remap node ${node.id} missing ${key} default.`);
2274
- }
2275
- });
2276
- });
2277
- return issues;
2278
- }
2
+ EXPRESSION_FUNCTION_VOCABULARY,
3
+ MACHINE_REPORT_VERSION,
4
+ PRIMARY_SLOT_ALIAS,
5
+ PRIMARY_SLOT_ID,
6
+ RESERVED_EXPRESSION_VARIABLES,
7
+ SCALAR_FUNCTIONS,
8
+ SCALAR_FUNCTION_VOCABULARY,
9
+ addBindingSlot,
10
+ bindingFromDefinition,
11
+ bindingTargetFromComponent,
12
+ bindingTargetFromInput,
13
+ bindingToDefinition,
14
+ buildCanonicalBindingExpression,
15
+ buildDefaultSlotExpression,
16
+ buildMachineReport,
17
+ buildPiecewiseRemapExpression,
18
+ buildRigGraphSpec,
19
+ collectExpressionReferences,
20
+ compileIrGraph,
21
+ createDefaultBinding,
22
+ createDefaultBindings,
23
+ createDefaultInputValues,
24
+ createDefaultParentBinding,
25
+ createDefaultRemap,
26
+ createExpressionVariableTable,
27
+ createIrGraphBuilder,
28
+ createLegacyIrGraph,
29
+ diffMachineReports,
30
+ ensureBindingStructure,
31
+ getPrimaryBindingSlot,
32
+ mapExpression,
33
+ parseControlExpression,
34
+ reconcileBindings,
35
+ remapValue,
36
+ removeBindingSlot,
37
+ toIrBindingSummary,
38
+ updateBindingExpression,
39
+ updateBindingSlotAlias,
40
+ updateBindingSlotRemap,
41
+ updateBindingSlotValueType,
42
+ updateBindingWithInput
43
+ } from "./chunk-XYPRZPXJ.js";
2279
44
  export {
45
+ EXPRESSION_FUNCTION_VOCABULARY,
46
+ MACHINE_REPORT_VERSION,
2280
47
  PRIMARY_SLOT_ALIAS,
2281
48
  PRIMARY_SLOT_ID,
49
+ RESERVED_EXPRESSION_VARIABLES,
50
+ SCALAR_FUNCTIONS,
51
+ SCALAR_FUNCTION_VOCABULARY,
2282
52
  addBindingSlot,
2283
53
  bindingFromDefinition,
2284
- bindingOperatorDefinitions,
2285
- bindingOperatorTypes,
2286
54
  bindingTargetFromComponent,
2287
55
  bindingTargetFromInput,
2288
56
  bindingToDefinition,
57
+ buildCanonicalBindingExpression,
58
+ buildDefaultSlotExpression,
59
+ buildMachineReport,
60
+ buildPiecewiseRemapExpression,
2289
61
  buildRigGraphSpec,
2290
62
  collectExpressionReferences,
63
+ compileIrGraph,
2291
64
  createDefaultBinding,
2292
65
  createDefaultBindings,
2293
66
  createDefaultInputValues,
2294
- createDefaultOperatorSettings,
2295
67
  createDefaultParentBinding,
2296
68
  createDefaultRemap,
69
+ createExpressionVariableTable,
70
+ createIrGraphBuilder,
71
+ createLegacyIrGraph,
72
+ diffMachineReports,
2297
73
  ensureBindingStructure,
2298
- ensureOperatorParams,
2299
- getBindingOperatorDefinition,
2300
74
  getPrimaryBindingSlot,
2301
75
  mapExpression,
2302
76
  parseControlExpression,
2303
77
  reconcileBindings,
2304
78
  remapValue,
2305
79
  removeBindingSlot,
2306
- setBindingOperatorEnabled,
80
+ toIrBindingSummary,
2307
81
  updateBindingExpression,
2308
- updateBindingOperatorParam,
2309
82
  updateBindingSlotAlias,
2310
83
  updateBindingSlotRemap,
84
+ updateBindingSlotValueType,
2311
85
  updateBindingWithInput
2312
86
  };