@things-factory/figure-ui 10.1.162 → 10.1.164

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.
Files changed (35) hide show
  1. package/client/modeller/data-interface.ts +334 -79
  2. package/client/modeller/editor.ts +4 -3
  3. package/client/modeller/figure-data-panel.ts +683 -193
  4. package/client/modeller/figure-preview.ts +46 -30
  5. package/client/modeller/refusals.ts +18 -14
  6. package/client/viewparts/figure-detail-dock.ts +9 -3
  7. package/dist-client/modeller/data-interface.d.ts +91 -30
  8. package/dist-client/modeller/data-interface.js +314 -71
  9. package/dist-client/modeller/data-interface.js.map +1 -1
  10. package/dist-client/modeller/editor.d.ts +1 -1
  11. package/dist-client/modeller/editor.js +3 -2
  12. package/dist-client/modeller/editor.js.map +1 -1
  13. package/dist-client/modeller/figure-data-panel.d.ts +42 -13
  14. package/dist-client/modeller/figure-data-panel.js +679 -186
  15. package/dist-client/modeller/figure-data-panel.js.map +1 -1
  16. package/dist-client/modeller/figure-preview.d.ts +7 -5
  17. package/dist-client/modeller/figure-preview.js +47 -30
  18. package/dist-client/modeller/figure-preview.js.map +1 -1
  19. package/dist-client/modeller/refusals.d.ts +2 -2
  20. package/dist-client/modeller/refusals.js +24 -18
  21. package/dist-client/modeller/refusals.js.map +1 -1
  22. package/dist-client/tsconfig.tsbuildinfo +1 -1
  23. package/dist-client/viewparts/figure-detail-dock.js +9 -3
  24. package/dist-client/viewparts/figure-detail-dock.js.map +1 -1
  25. package/package.json +7 -7
  26. package/test/browser/build.mjs +9 -0
  27. package/test/browser/detail-tabs-page.js +36 -0
  28. package/test/browser/detail-tabs.check.mjs +123 -0
  29. package/test/data-motion.test.ts +173 -53
  30. package/translations/en.json +122 -43
  31. package/translations/es.json +122 -43
  32. package/translations/ja.json +122 -43
  33. package/translations/ko.json +122 -43
  34. package/translations/ms.json +122 -43
  35. package/translations/zh.json +122 -43
@@ -1,42 +1,71 @@
1
1
  /*
2
- * The figure's public data properties and what each moves (ADR-0051 §2.2): what the data tab offers and shows. Every
3
- * rule is figure-model's -- which transform a property may take, whether a connection holds, what a sample moves; this
4
- * only lists what there is to choose and drafts a first connection the kernel then checks. Kept apart from the element so
5
- * it is tested without a browser.
2
+ * The figure's data and what it moves (ADR-0051 §8, data binding design appendix A): what the data tab offers and
3
+ * shows. Every rule is figure-model's -- which accessor holds, which rule's results a behavior takes -- and the reading
4
+ * of data is things-scene's; this only lists what there is to choose, drafts a binding the kernel then checks, and edits
5
+ * the schema as a tree. Kept apart from the element so it is tested without a browser.
6
6
  */
7
- import { compileFigureDataInterface, evaluateFigureDataInterface, validateFigureDataValues, FIGURE_PART_ID } from '@hatiolab/figure-model';
8
- export const targetKeyOf = (t) => t.kind === 'state' ? `state:${t.input}` : t.kind === 'palette' ? `palette:${t.token}` : `${t.kind}:${t.group}`;
7
+ import { FIGURE_PART_ID, FIGURE_MOTION_SPEED_MAX, FigureContractError, figureDataRows, parseFigureAccessor } from '@hatiolab/figure-model';
8
+ import { FigureInputs } from '@hatiolab/things-scene';
9
+ export const behaviorKeyOf = (b) => b.kind === 'state'
10
+ ? `state:${b.input}`
11
+ : b.kind === 'palette'
12
+ ? `palette:${b.token}`
13
+ : b.kind === 'status'
14
+ ? `status:${b.port}`
15
+ : b.kind === 'pose'
16
+ ? `pose:${b.axis}`
17
+ : b.kind === 'items'
18
+ ? `items:${b.slot ?? ''}`
19
+ : `${b.kind}:${b.group}`;
9
20
  /**
10
- * What a property of this kind can be connected to: a number moves a control or a group's speed; an enum or a boolean
11
- * moves a control, a colour or which clip of a group plays (through a case per value). A control is offered observed: a
12
- * value read from the machine applies as it comes.
21
+ * Everything the figure can do that data may drive: each control, each colour, each motion group's clip, speed and
22
+ * pause, each status port, its position and heading, and -- when it has places -- the items in them.
13
23
  */
14
- export function dataTargetsOf(asset, kind) {
24
+ export function behaviorsOf(asset) {
15
25
  const out = [];
26
+ const add = (behavior, name, more = {}) => out.push({ key: behaviorKeyOf(behavior), behavior, kind: behavior.kind, name, ...more });
16
27
  for (const input of asset.document.model.inputs) {
17
28
  if (input.role !== 'state')
18
29
  continue;
19
- const target = { kind: 'state', input: input.id, sourceMode: 'observed' };
20
- out.push({ key: targetKeyOf(target), target, name: asset.stateInputs?.[input.id]?.label ?? input.id, unit: input.unit, min: input.min, max: input.max });
30
+ add({ kind: 'state', input: input.id, sourceMode: 'observed' }, asset.stateInputs?.[input.id]?.label ?? input.id, { unit: input.unit, min: input.min, max: input.max });
21
31
  }
22
- if (kind !== 'number')
23
- for (const token of Object.keys(asset.palette)) {
24
- const target = { kind: 'palette', token };
25
- out.push({ key: targetKeyOf(target), target, name: token });
26
- }
32
+ for (const token of Object.keys(asset.palette))
33
+ add({ kind: 'palette', token }, token);
27
34
  for (const group of asset.motionGroups ?? []) {
28
- const target = kind === 'number' ? { kind: 'motion-speed', group: group.id } : { kind: 'motion-clip', group: group.id };
29
- out.push({ key: targetKeyOf(target), target, name: group.label });
35
+ add({ kind: 'motion-clip', group: group.id }, group.label);
36
+ add({ kind: 'motion-speed', group: group.id }, group.label, { min: 0, max: FIGURE_MOTION_SPEED_MAX });
37
+ add({ kind: 'motion-paused', group: group.id }, group.label);
30
38
  }
39
+ for (const port of asset.dataInterface?.ports ?? [])
40
+ add({ kind: 'status', port: port.id }, port.label);
41
+ for (const axis of ['x', 'z', 'y', 'heading'])
42
+ add({ kind: 'pose', axis, seconds: 1 }, axis, { unit: axis === 'heading' ? 'deg' : 'mm' });
43
+ if (asset.document.capabilities.slotLayout || asset.document.capabilities.names?.includes('Holdable'))
44
+ add({ kind: 'items' }, asset.document.capabilities.slotLayout?.label ?? 'items');
31
45
  return out;
32
46
  }
33
- /** The values a lookup has a case for: an enum's values, or both booleans. Nothing for a number. */
34
- export const domainOf = (property) => property.kind === 'enum' ? [...property.values] : property.kind === 'boolean' ? [false, true] : [];
47
+ const isValue = (n) => n.kind === 'number' || n.kind === 'boolean' || n.kind === 'text';
48
+ /** Whether a field of this kind can drive the behavior, by some rule -- the choices offered for it. */
49
+ export function fitsField(node, choice) {
50
+ if (choice.kind === 'items')
51
+ return node.kind === 'list' || node.kind === 'text' || node.kind === 'number';
52
+ if (!isValue(node))
53
+ return false;
54
+ if (choice.kind === 'pose')
55
+ return node.kind === 'number';
56
+ if (choice.kind === 'motion-speed')
57
+ return node.kind === 'number' || node.kind === 'boolean';
58
+ if (choice.kind === 'motion-paused')
59
+ return node.kind === 'boolean' || (node.kind === 'text' && !!node.values);
60
+ if (choice.kind === 'status')
61
+ return node.kind !== 'boolean';
62
+ return true;
63
+ }
35
64
  /** An id not taken among `taken`: `base`, then `base-2`, `base-3`, … made into the kernel's id shape. */
36
- export function freeDataIdOf(taken, base) {
65
+ export function freeIdOf(taken, base) {
37
66
  const used = new Set(taken);
38
- const cleaned = base.replace(/[^a-zA-Z0-9_-]+/g, '-').replace(/^[^a-zA-Z]+/, '').replace(/-+$/, '') || 'data';
39
- const id = FIGURE_PART_ID.test(cleaned) ? cleaned : 'data';
67
+ const cleaned = base.replace(/[^a-zA-Z0-9_-]+/g, '-').replace(/^[^a-zA-Z]+/, '').replace(/-+$/, '') || 'binding';
68
+ const id = FIGURE_PART_ID.test(cleaned) ? cleaned : 'binding';
40
69
  if (!used.has(id))
41
70
  return id;
42
71
  let n = 2;
@@ -44,70 +73,284 @@ export function freeDataIdOf(taken, base) {
44
73
  n++;
45
74
  return `${id}-${n}`;
46
75
  }
76
+ /** How many mm, or degrees, one of a unit is: only lengths and angles are converted. */
77
+ const TO_MM = { mm: 1, cm: 10, m: 1000 };
78
+ const TO_DEG = { deg: 1, rad: 180 / Math.PI };
79
+ /** The values a closed field takes: a closed text's values, both booleans. Empty when open. */
80
+ export const domainOf = (node) => (node.kind === 'boolean' ? [false, true] : node.kind === 'text' && node.values ? [...node.values] : []);
47
81
  /**
48
- * A first connection of a property to a target, for the author to adjust: a number as it is in the control's own unit,
49
- * or scaled from another; an enum or a boolean case by case, each case at what the target shows now -- the control's
50
- * rest value, the colour, the group's first clip. The kernel says whether it holds.
82
+ * A first binding of a field to a behavior, for the author to adjust: the value as it is when it already fits, a scale
83
+ * when only its unit or range differs, one result per value (a map) for a closed field, a default for an open one.
84
+ * Each result starts at what the behavior shows now -- the control's rest value, the colour, the group's default clip --
85
+ * or, for a clip, the clip of the value's own name. The kernel says whether it holds.
51
86
  */
52
- export function connectionDraftOf(asset, property, choice) {
53
- const taken = (asset.dataInterface?.connections ?? []).map(c => c.id);
54
- const id = freeDataIdOf(taken, `${property.id}-${choice.target.kind === 'state' ? choice.target.input : choice.target.kind === 'palette' ? 'colour' : choice.target.group}`);
55
- const target = choice.target;
56
- if (property.kind === 'number') {
57
- const same = target.kind === 'motion-speed' || (target.kind === 'state' && choice.unit === property.unit);
58
- return { id, property: property.id, transform: same ? { kind: 'identity' } : { kind: 'affine', scale: 1, offset: 0 }, target };
87
+ export function bindingDraftOf(asset, accessor, node, choice) {
88
+ const taken = (asset.dataInterface?.bindings ?? []).map(b => b.id);
89
+ const name = accessor.replace(/\[\d+\]/g, '').split('.').pop() ?? accessor;
90
+ const id = freeIdOf(taken, `${name}-${choice.kind === 'state' ? choice.behavior.input : choice.kind}`);
91
+ const behavior = choice.behavior;
92
+ const make = (rule, param) => ({ id, accessor, behavior, rule, ...(param ? { param } : {}) });
93
+ const domain = domainOf(node);
94
+ const caseMap = (each, fallback) => Object.fromEntries([...domain.map(v => [String(v), each(v)]), ...(domain.length ? [] : [['default', fallback]])]);
95
+ if (behavior.kind === 'items') {
96
+ if (node.kind !== 'list')
97
+ return make('value');
98
+ const fields = node.item.kind === 'object' ? node.item.fields : [];
99
+ const pick = (...words) => fields.find(f => isValue(f) && words.some(w => w.test(f.name)))?.name;
100
+ const itemId = pick(/^id$/i, /name$/i, /id$/i) ?? fields.find(isValue)?.name;
101
+ const type = pick(/type$/i);
102
+ const place = pick(/^slot/i, /place/i, /port/i);
103
+ const stack = pick(/stack/i, /level/i);
104
+ return { id, accessor, behavior: { kind: 'items', ...(itemId ? { id: itemId } : {}), ...(type ? { type } : {}), ...(place ? { place } : {}), ...(stack ? { stack } : {}) }, rule: 'value' };
105
+ }
106
+ if (behavior.kind === 'status')
107
+ return make('value');
108
+ if (behavior.kind === 'palette') {
109
+ const now = asset.palette[behavior.token] ?? '#000000';
110
+ if (node.kind === 'number')
111
+ return make('range', { default: now });
112
+ return make('map', caseMap(() => now, now));
113
+ }
114
+ if (behavior.kind === 'motion-clip') {
115
+ const group = (asset.motionGroups ?? []).find(g => g.id === behavior.group);
116
+ const clips = group?.clips ?? [];
117
+ if (node.kind === 'text' && node.values && node.values.every(v => clips.includes(v)))
118
+ return make('value');
119
+ return make('map', caseMap(v => (clips.includes(String(v)) ? String(v) : (group?.defaultClip ?? '')), group?.defaultClip ?? ''));
120
+ }
121
+ if (behavior.kind === 'motion-paused') {
122
+ if (node.kind === 'boolean')
123
+ return make('value');
124
+ return make('map', caseMap(() => false, false));
125
+ }
126
+ if (behavior.kind === 'pose') {
127
+ if (node.kind !== 'number')
128
+ return make('value');
129
+ const want = behavior.axis === 'heading' ? TO_DEG : TO_MM;
130
+ const unit = node.unit ?? '';
131
+ if (unit === (behavior.axis === 'heading' ? 'deg' : 'mm'))
132
+ return make('value');
133
+ return unit in want ? make('scale', { scale: want[unit], offset: 0 }) : make('value');
134
+ }
135
+ /* state and motion-speed: a number into a range. */
136
+ const [lo, hi, unit] = behavior.kind === 'motion-speed' ? [0, FIGURE_MOTION_SPEED_MAX, 'ratio'] : [choice.min ?? 0, choice.max ?? 1, choice.unit ?? ''];
137
+ if (node.kind === 'number') {
138
+ const sameUnit = behavior.kind === 'motion-speed' ? node.unit === undefined || node.unit === 'ratio' : node.unit === unit;
139
+ const inside = node.min !== undefined && node.max !== undefined && node.min >= lo && node.max <= hi;
140
+ if (sameUnit && inside)
141
+ return make('value');
142
+ if (node.min !== undefined && node.max !== undefined && node.max > node.min) {
143
+ /* The field's range laid onto the behavior's. */
144
+ const scale = (hi - lo) / (node.max - node.min);
145
+ return make('scale', { scale: round(scale), offset: round(lo - scale * node.min) });
146
+ }
147
+ return make('value');
148
+ }
149
+ const rest = behavior.kind === 'motion-speed' ? 1 : (asset.stateDefaults[behavior.input] ?? lo);
150
+ if (node.kind === 'boolean')
151
+ return make('map', { false: behavior.kind === 'motion-speed' ? 0 : lo, true: behavior.kind === 'motion-speed' ? 1 : hi });
152
+ return make('map', caseMap(() => rest, rest));
153
+ }
154
+ const round = (v) => Math.round(v * 1e6) / 1e6;
155
+ /* ---------------------------------------------------------------- the schema as a tree */
156
+ /** A row's path (`L[].F`) as the accessor a value binding reads (`L[0].F`): the first element of each list. */
157
+ export const accessorOfRow = (path) => path.replace(/\[\]/g, '[0]');
158
+ /** The field at a row's path, or undefined. */
159
+ export function fieldAt(schema, path) {
160
+ let list = schema;
161
+ let found;
162
+ for (const part of path.split('.')) {
163
+ const name = part.replace(/\[\]/g, '');
164
+ found = list.find(f => f.name === name);
165
+ if (!found)
166
+ return undefined;
167
+ let node = found;
168
+ while (node.kind === 'list')
169
+ node = node.item;
170
+ list = node.kind === 'object' ? node.fields : [];
59
171
  }
60
- const now = target.kind === 'state'
61
- ? (asset.stateDefaults[target.input] ?? 0)
62
- : target.kind === 'palette'
63
- ? (asset.palette[target.token] ?? '#000000')
64
- : ((asset.motionGroups ?? []).find(g => g.id === target.group)?.defaultClip ?? '');
65
- return { id, property: property.id, transform: { kind: 'lookup', cases: domainOf(property).map(when => ({ when, value: now })) }, target };
66
- }
67
- /** What a property looks like when first made: a number 0 to 1, a boolean off, an enum of two values. */
68
- export function propertyDraftOf(asset, label, kind) {
69
- const id = freeDataIdOf((asset.dataInterface?.properties ?? []).map(p => p.id), label);
70
- if (kind === 'number')
71
- return { id, label, kind, unit: 'ratio', min: 0, max: 1, default: 0 };
72
- if (kind === 'boolean')
73
- return { id, label, kind, default: false };
74
- return { id, label, kind, values: ['off', 'on'], default: 'off' };
75
- }
76
- /** A value typed in a sample field, as the property takes it: a number, true or false, or the enum value. */
77
- export function sampleValueOf(property, typed) {
78
- if (property.kind === 'number')
172
+ return found;
173
+ }
174
+ /** The fields under a row's path, where a new field goes: the top, an object's fields, a list's element's fields. */
175
+ function fieldsUnder(schema, path) {
176
+ if (!path)
177
+ return schema;
178
+ const field = fieldAt(schema, path);
179
+ if (!field)
180
+ return undefined;
181
+ let node = field;
182
+ while (node.kind === 'list')
183
+ node = node.item;
184
+ return node.kind === 'object' ? node.fields : undefined;
185
+ }
186
+ /** The schema with a field added under a path ('' for the top): a copy, the given one left as it is. */
187
+ export function schemaWithField(schema, under, field) {
188
+ const out = structuredClone(schema);
189
+ fieldsUnder(out, under)?.push(field);
190
+ return out;
191
+ }
192
+ /** The schema with the field at a path replaced (its name kept unless the new one says another). */
193
+ export function schemaWithFieldReplaced(schema, path, field) {
194
+ const out = structuredClone(schema);
195
+ const parent = path.includes('.') ? path.slice(0, path.lastIndexOf('.')) : '';
196
+ const list = fieldsUnder(out, parent);
197
+ const name = path.split('.').pop().replace(/\[\]/g, '');
198
+ const at = list?.findIndex(f => f.name === name) ?? -1;
199
+ if (list && at >= 0)
200
+ list[at] = field;
201
+ return out;
202
+ }
203
+ /** The schema without the field at a path. */
204
+ export function schemaWithoutField(schema, path) {
205
+ const out = structuredClone(schema);
206
+ const parent = path.includes('.') ? path.slice(0, path.lastIndexOf('.')) : '';
207
+ const list = fieldsUnder(out, parent);
208
+ const name = path.split('.').pop().replace(/\[\]/g, '');
209
+ const at = list?.findIndex(f => f.name === name) ?? -1;
210
+ if (list && at >= 0)
211
+ list.splice(at, 1);
212
+ return out;
213
+ }
214
+ /** A field of another kind, its name, label and json flag kept, the rest started fresh. */
215
+ export function fieldOfKind(field, kind) {
216
+ const head = { name: field.name, ...(field.label ? { label: field.label } : {}) };
217
+ if (kind === 'object')
218
+ return { ...head, kind, fields: field.kind === 'object' ? field.fields : [] };
219
+ if (kind === 'list')
220
+ return { ...head, kind, item: field.kind === 'list' ? field.item : { kind: 'object', fields: [] } };
221
+ return { ...head, kind };
222
+ }
223
+ /** The rows of the schema, each with the bindings that read it (figure-model's view). */
224
+ export const schemaRowsOf = (asset) => (asset.dataInterface ? figureDataRows(asset.dataInterface) : []);
225
+ /* ---------------------------------------------------------------- reading a sample */
226
+ /**
227
+ * What a sample of data would do on this figure, read the one way a placed figure reads it (things-scene's figure
228
+ * inputs): what it moves, or why it is refused, each value's reason. Nothing here reads data itself.
229
+ */
230
+ export function sampleOf(asset, sample) {
231
+ const inputs = new FigureInputs(asset);
232
+ const taken = inputs.setData(sample);
233
+ return { issues: taken.accepted ? [] : (taken.issues[0]?.issues ?? []), result: inputs.data };
234
+ }
235
+ /** The rows a person gives a value by hand, as a board's saved values: one value, read by a binding, not inside a list. */
236
+ export const valueRowsOf = (asset) => schemaRowsOf(asset).filter(r => isValue(r.node) && r.bindings.length > 0 && !r.path.includes('[]'));
237
+ /** A value typed or chosen for a row, as its kind reads it: a number (NaN when empty), true or false, or the text. */
238
+ export function typedValueOf(node, typed) {
239
+ if (node.kind === 'number')
79
240
  return typed.trim() === '' ? Number.NaN : Number(typed);
80
- if (property.kind === 'boolean')
241
+ if (node.kind === 'boolean')
81
242
  return typed === 'true';
82
243
  return typed;
83
244
  }
84
- /**
85
- * What a sample of values would move, as the figure would run it: the kernel's check and evaluation of the interface.
86
- * Refused whole when one value cannot be taken, with each one's reason, as a live snapshot is.
87
- */
88
- export function dataSampleOf(asset, values) {
89
- const plan = compileFigureDataInterface(asset);
90
- if (!plan)
91
- return { issues: [] };
92
- const issues = validateFigureDataValues(plan, values);
93
- return issues.length ? { issues } : { result: evaluateFigureDataInterface(plan, values) };
245
+ /** The parts a status port may show on: the parts the figure's appearance names. */
246
+ export const partsOf = (asset) => [...new Set(asset.appearance.map(a => a.target))];
247
+ /** A first status port: the label as given, on the first colour token, no part yet. The kernel says whether it holds. */
248
+ export function portDraftOf(asset, label) {
249
+ const taken = (asset.dataInterface?.ports ?? []).map(p => p.id);
250
+ const token = Object.keys(asset.palette)[0];
251
+ return { id: freeIdOf(taken, label.trim() || 'status'), label: label.trim(), tokens: token ? [token] : [], parts: [] };
94
252
  }
253
+ /** Whether a text is an accessor the kernel takes. */
254
+ export const accessorTaken = (accessor) => parseFigureAccessor(accessor) !== null;
95
255
  /**
96
- * Which fields of each motion group the data interface writes: its clip (a lookup to `motion-clip`), its speed
97
- * (`motion-speed`). The data always writes them -- from a live value, a saved one, or the property's default -- and
98
- * wins over a group's own setting for that field (detail design §5.1), so a control for such a field moves nothing.
256
+ * Which fields of each motion group the data binds: its clip, its speed. A field bound wins over the group's own setting
257
+ * whenever the data, a saved value or the schema's default gives it a value, so the preview drives it through the data.
99
258
  */
100
259
  export function motionWrittenByData(asset) {
101
260
  const out = {};
102
- for (const c of asset.dataInterface?.connections ?? []) {
103
- if (c.target.kind !== 'motion-clip' && c.target.kind !== 'motion-speed')
261
+ for (const b of asset.dataInterface?.bindings ?? []) {
262
+ if (b.behavior.kind !== 'motion-clip' && b.behavior.kind !== 'motion-speed')
104
263
  continue;
105
- const one = (out[c.target.group] ??= { clip: false, speed: false });
106
- if (c.target.kind === 'motion-clip')
264
+ const one = (out[b.behavior.group] ??= { clip: false, speed: false });
265
+ if (b.behavior.kind === 'motion-clip')
107
266
  one.clip = true;
108
267
  else
109
268
  one.speed = true;
110
269
  }
111
270
  return out;
112
271
  }
272
+ /** The joints that turn without end: revolute, with no limits, leading no one's mimic. */
273
+ export function endlessJointsOf(asset) {
274
+ const out = [];
275
+ for (const j of asset.joints ?? []) {
276
+ if (j.type !== 'revolute' || j.lowerLimit !== undefined || j.upperLimit !== undefined || j.mimic)
277
+ continue;
278
+ const input = j.state?.id ?? j.id;
279
+ const unit = asset.document.model.inputs.find(i => i.id === input)?.unit;
280
+ if (unit !== 'deg' && unit !== 'rad')
281
+ continue;
282
+ if (!out.some(e => e.input === input))
283
+ out.push({ input, label: asset.stateInputs?.[input]?.label ?? input, unit });
284
+ }
285
+ return out;
286
+ }
287
+ /**
288
+ * The rotation card's commands (data binding design decision 1, 「회전」): a clip that turns the joint once around and
289
+ * keeps adding the turn (wrap, accumulating), a reverse one when a direction is given, the motion group holding them,
290
+ * and the bindings -- on to the group's pause, speed to its speed, direction to its clip. Nothing new runs them: they
291
+ * are the drivers, the group and the bindings any author could write, and stay editable one by one.
292
+ *
293
+ * The clip lasts `T = 3 × 60 / max rpm` seconds, so the most the field gives plays at the group's top speed, 3 -- the
294
+ * speed range is not widened. One turn per `T` at speed 1, and speed = rpm × T / 60. A stop is a pause: the joint stays
295
+ * where it is, where a stopped clip would jump back to its first key.
296
+ */
297
+ export function rotationStepsOf(asset, c) {
298
+ const joint = endlessJointsOf(asset).find(j => j.input === c.input);
299
+ if (!joint)
300
+ throw new FigureContractError('TARGET_ABSENT', c.input, `${c.input} is not a joint that turns without end`);
301
+ const turn = joint.unit === 'deg' ? 360 : 2 * Math.PI;
302
+ const seconds = c.speed && c.speed.max > 0 ? round((3 * 60) / c.speed.max) : 1;
303
+ const group = freeIdOf((asset.motionGroups ?? []).map(g => g.id), `${joint.input}-spin`);
304
+ const forward = group;
305
+ const reverse = `${group}-reverse`;
306
+ const steps = [];
307
+ const schema = asset.dataInterface?.schema ?? [];
308
+ if (c.speed) {
309
+ const field = fieldAt(schema, c.speed.path);
310
+ if (field?.kind === 'number' && (field.min === undefined || field.max === undefined))
311
+ steps.push({ kind: 'set-data-schema', schema: schemaWithFieldReplaced(schema, c.speed.path, { ...field, min: field.min ?? 0, max: field.max ?? c.speed.max }) });
312
+ }
313
+ const spin = (clip, way, inGroup) => ({
314
+ kind: 'set-driver',
315
+ state: joint.input,
316
+ clip,
317
+ duration: seconds,
318
+ keys: [
319
+ { seconds: 0, value: 0 },
320
+ { seconds, value: way * turn }
321
+ ],
322
+ interpolation: 'linear',
323
+ loop: 'wrap',
324
+ accumulates: true,
325
+ ...(inGroup ? { group } : {})
326
+ });
327
+ const clips = c.direction ? [forward, reverse] : [forward];
328
+ steps.push(spin(forward, 1, false));
329
+ steps.push({ kind: 'set-motion-group', group: { id: group, label: c.label, clips: [forward], defaultClip: forward, transitionSeconds: 0 } });
330
+ if (c.direction) {
331
+ steps.push(spin(reverse, -1, true));
332
+ steps.push({ kind: 'set-motion-group', group: { id: group, label: c.label, clips, defaultClip: forward, transitionSeconds: 0 } });
333
+ }
334
+ const taken = (asset.dataInterface?.bindings ?? []).map(b => b.id);
335
+ const bind = (base, binding) => {
336
+ const id = freeIdOf(taken, base);
337
+ taken.push(id);
338
+ return { kind: 'set-data-binding', binding: { id, ...binding } };
339
+ };
340
+ if (c.on)
341
+ steps.push(bind(`${group}-on`, { accessor: c.on, behavior: { kind: 'motion-paused', group }, rule: 'map', param: { true: false, false: true } }));
342
+ if (c.speed)
343
+ steps.push(bind(`${group}-speed`, { accessor: c.speed.path, behavior: { kind: 'motion-speed', group }, rule: 'scale', param: { scale: round(seconds / 60), offset: 0 } }));
344
+ if (c.direction) {
345
+ const values = domainOf(fieldAt(schema, c.direction.path) ?? { kind: 'text' }).map(String);
346
+ const covered = values.every(v => v === c.direction.forward || v === c.direction.reverse);
347
+ steps.push(bind(`${group}-direction`, {
348
+ accessor: c.direction.path,
349
+ behavior: { kind: 'motion-clip', group },
350
+ rule: 'map',
351
+ param: { [c.direction.forward]: forward, [c.direction.reverse]: reverse, ...(covered ? {} : { default: forward }) }
352
+ }));
353
+ }
354
+ return steps;
355
+ }
113
356
  //# sourceMappingURL=data-interface.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"data-interface.js","sourceRoot":"","sources":["../../client/modeller/data-interface.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The figure's public data properties and what each moves (ADR-0051 §2.2): what the data tab offers and shows. Every\n * rule is figure-model's -- which transform a property may take, whether a connection holds, what a sample moves; this\n * only lists what there is to choose and drafts a first connection the kernel then checks. Kept apart from the element so\n * it is tested without a browser.\n */\nimport { compileFigureDataInterface, evaluateFigureDataInterface, validateFigureDataValues, FIGURE_PART_ID } from '@hatiolab/figure-model'\nimport type { FigureAsset, FigureDataConnection, FigureDataProperty, FigureDataResult, FigureDataTarget, FigureDataValue, FigureDataValueIssue } from '@hatiolab/figure-model'\n\n/** Something a connection can write, with what the screen shows of it. */\nexport interface DataTargetChoice {\n /** One string per target, for a select's value. */\n key: string\n target: FigureDataTarget\n /** The control's label, the colour token, or the group's label. */\n name: string\n unit?: string\n min?: number\n max?: number\n}\n\nexport const targetKeyOf = (t: FigureDataTarget): string =>\n t.kind === 'state' ? `state:${t.input}` : t.kind === 'palette' ? `palette:${t.token}` : `${t.kind}:${t.group}`\n\n/**\n * What a property of this kind can be connected to: a number moves a control or a group's speed; an enum or a boolean\n * moves a control, a colour or which clip of a group plays (through a case per value). A control is offered observed: a\n * value read from the machine applies as it comes.\n */\nexport function dataTargetsOf(asset: FigureAsset, kind: FigureDataProperty['kind']): DataTargetChoice[] {\n const out: DataTargetChoice[] = []\n for (const input of asset.document.model.inputs) {\n if (input.role !== 'state') continue\n const target: FigureDataTarget = { kind: 'state', input: input.id, sourceMode: 'observed' }\n out.push({ key: targetKeyOf(target), target, name: asset.stateInputs?.[input.id]?.label ?? input.id, unit: input.unit, min: input.min, max: input.max })\n }\n if (kind !== 'number')\n for (const token of Object.keys(asset.palette)) {\n const target: FigureDataTarget = { kind: 'palette', token }\n out.push({ key: targetKeyOf(target), target, name: token })\n }\n for (const group of asset.motionGroups ?? []) {\n const target: FigureDataTarget = kind === 'number' ? { kind: 'motion-speed', group: group.id } : { kind: 'motion-clip', group: group.id }\n out.push({ key: targetKeyOf(target), target, name: group.label })\n }\n return out\n}\n\n/** The values a lookup has a case for: an enum's values, or both booleans. Nothing for a number. */\nexport const domainOf = (property: FigureDataProperty): FigureDataValue[] =>\n property.kind === 'enum' ? [...property.values] : property.kind === 'boolean' ? [false, true] : []\n\n/** An id not taken among `taken`: `base`, then `base-2`, `base-3`, … made into the kernel's id shape. */\nexport function freeDataIdOf(taken: Iterable<string>, base: string): string {\n const used = new Set(taken)\n const cleaned = base.replace(/[^a-zA-Z0-9_-]+/g, '-').replace(/^[^a-zA-Z]+/, '').replace(/-+$/, '') || 'data'\n const id = FIGURE_PART_ID.test(cleaned) ? cleaned : 'data'\n if (!used.has(id)) return id\n let n = 2\n while (used.has(`${id}-${n}`)) n++\n return `${id}-${n}`\n}\n\n/**\n * A first connection of a property to a target, for the author to adjust: a number as it is in the control's own unit,\n * or scaled from another; an enum or a boolean case by case, each case at what the target shows now -- the control's\n * rest value, the colour, the group's first clip. The kernel says whether it holds.\n */\nexport function connectionDraftOf(asset: FigureAsset, property: FigureDataProperty, choice: DataTargetChoice): FigureDataConnection {\n const taken = (asset.dataInterface?.connections ?? []).map(c => c.id)\n const id = freeDataIdOf(taken, `${property.id}-${choice.target.kind === 'state' ? choice.target.input : choice.target.kind === 'palette' ? 'colour' : choice.target.group}`)\n const target = choice.target\n if (property.kind === 'number') {\n const same = target.kind === 'motion-speed' || (target.kind === 'state' && choice.unit === property.unit)\n return { id, property: property.id, transform: same ? { kind: 'identity' } : { kind: 'affine', scale: 1, offset: 0 }, target }\n }\n const now: FigureDataValue =\n target.kind === 'state'\n ? (asset.stateDefaults[target.input] ?? 0)\n : target.kind === 'palette'\n ? (asset.palette[target.token] ?? '#000000')\n : ((asset.motionGroups ?? []).find(g => g.id === target.group)?.defaultClip ?? '')\n return { id, property: property.id, transform: { kind: 'lookup', cases: domainOf(property).map(when => ({ when, value: now })) }, target }\n}\n\n/** What a property looks like when first made: a number 0 to 1, a boolean off, an enum of two values. */\nexport function propertyDraftOf(asset: FigureAsset, label: string, kind: FigureDataProperty['kind']): FigureDataProperty {\n const id = freeDataIdOf((asset.dataInterface?.properties ?? []).map(p => p.id), label)\n if (kind === 'number') return { id, label, kind, unit: 'ratio', min: 0, max: 1, default: 0 }\n if (kind === 'boolean') return { id, label, kind, default: false }\n return { id, label, kind, values: ['off', 'on'], default: 'off' }\n}\n\n/** A value typed in a sample field, as the property takes it: a number, true or false, or the enum value. */\nexport function sampleValueOf(property: FigureDataProperty, typed: string): FigureDataValue {\n if (property.kind === 'number') return typed.trim() === '' ? Number.NaN : Number(typed)\n if (property.kind === 'boolean') return typed === 'true'\n return typed\n}\n\n/**\n * What a sample of values would move, as the figure would run it: the kernel's check and evaluation of the interface.\n * Refused whole when one value cannot be taken, with each one's reason, as a live snapshot is.\n */\nexport function dataSampleOf(asset: FigureAsset, values: Record<string, FigureDataValue>): { result: FigureDataResult } | { issues: FigureDataValueIssue[] } {\n const plan = compileFigureDataInterface(asset)\n if (!plan) return { issues: [] }\n const issues = validateFigureDataValues(plan, values)\n return issues.length ? { issues } : { result: evaluateFigureDataInterface(plan, values) }\n}\n\n/**\n * Which fields of each motion group the data interface writes: its clip (a lookup to `motion-clip`), its speed\n * (`motion-speed`). The data always writes them -- from a live value, a saved one, or the property's default -- and\n * wins over a group's own setting for that field (detail design §5.1), so a control for such a field moves nothing.\n */\nexport function motionWrittenByData(asset: FigureAsset): Record<string, { clip: boolean; speed: boolean }> {\n const out: Record<string, { clip: boolean; speed: boolean }> = {}\n for (const c of asset.dataInterface?.connections ?? []) {\n if (c.target.kind !== 'motion-clip' && c.target.kind !== 'motion-speed') continue\n const one = (out[c.target.group] ??= { clip: false, speed: false })\n if (c.target.kind === 'motion-clip') one.clip = true\n else one.speed = true\n }\n return out\n}\n\n"]}
1
+ {"version":3,"file":"data-interface.js","sourceRoot":"","sources":["../../client/modeller/data-interface.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAE1I,OAAO,EAAE,YAAY,EAAyB,MAAM,wBAAwB,CAAA;AAgB5E,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAqB,EAAU,EAAE,CAC7D,CAAC,CAAC,IAAI,KAAK,OAAO;IAChB,CAAC,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE;IACpB,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS;QACpB,CAAC,CAAC,WAAW,CAAC,CAAC,KAAK,EAAE;QACtB,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ;YACnB,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,EAAE;YACpB,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM;gBACjB,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE;gBAClB,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO;oBAClB,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,IAAI,EAAE,EAAE;oBACzB,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,EAAE,CAAA;AAEpC;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,KAAkB;IAC5C,MAAM,GAAG,GAAqB,EAAE,CAAA;IAChC,MAAM,GAAG,GAAG,CAAC,QAA4B,EAAE,IAAY,EAAE,OAAgC,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,aAAa,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC,CAAA;IACxL,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAChD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;YAAE,SAAQ;QACpC,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,KAAK,IAAI,KAAK,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAA;IACzK,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;QAAE,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,KAAK,CAAC,CAAA;IACtF,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,YAAY,IAAI,EAAE,EAAE,CAAC;QAC7C,GAAG,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;QAC1D,GAAG,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,uBAAuB,EAAE,CAAC,CAAA;QACrG,GAAG,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IAC9D,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,aAAa,EAAE,KAAK,IAAI,EAAE;QAAE,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;IACvG,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,CAAU;QAAE,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;IAClJ,IAAI,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,UAAU,IAAI,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC;QAAE,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,UAAU,EAAE,KAAK,IAAI,OAAO,CAAC,CAAA;IACvL,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,MAAM,OAAO,GAAG,CAAC,CAAiB,EAAyE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,CAAA;AAE9K,uGAAuG;AACvG,MAAM,UAAU,SAAS,CAAC,IAAoB,EAAE,MAAsB;IACpE,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAA;IAC1G,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAA;IAChC,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAA;IACzD,IAAI,MAAM,CAAC,IAAI,KAAK,cAAc;QAAE,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,CAAA;IAC5F,IAAI,MAAM,CAAC,IAAI,KAAK,eAAe;QAAE,OAAO,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAC9G,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,IAAI,KAAK,SAAS,CAAA;IAC5D,OAAO,IAAI,CAAA;AACb,CAAC;AAED,yGAAyG;AACzG,MAAM,UAAU,QAAQ,CAAC,KAAuB,EAAE,IAAY;IAC5D,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAA;IAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,SAAS,CAAA;IAChH,MAAM,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAA;IAC7D,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAAE,OAAO,EAAE,CAAA;IAC5B,IAAI,CAAC,GAAG,CAAC,CAAA;IACT,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC;QAAE,CAAC,EAAE,CAAA;IAClC,OAAO,GAAG,EAAE,IAAI,CAAC,EAAE,CAAA;AACrB,CAAC;AAED,wFAAwF;AACxF,MAAM,KAAK,GAA2B,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,CAAA;AAChE,MAAM,MAAM,GAA2B,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC,EAAE,EAAE,CAAA;AAErE,+FAA+F;AAC/F,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAoB,EAAqB,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;AAE5K;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,KAAkB,EAAE,QAAgB,EAAE,IAAoB,EAAE,MAAsB;IAC/G,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,aAAa,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAClE,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,QAAQ,CAAA;IAC1E,MAAM,EAAE,GAAG,QAAQ,CAAC,KAAK,EAAE,GAAG,IAAI,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAE,MAAM,CAAC,QAA8B,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IAC7H,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAA;IAChC,MAAM,IAAI,GAAG,CAAC,IAA+B,EAAE,KAAkC,EAAqB,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;IACxK,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;IAC7B,MAAM,OAAO,GAAG,CAAC,IAA6C,EAAE,QAAyB,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAU,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAU,CAAC,CAAC,CAAC,CAAC,CAAA;IAEjO,IAAI,QAAQ,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC9B,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC,CAAA;QAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;QAClE,MAAM,IAAI,GAAG,CAAC,GAAG,KAAe,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAA;QAC1G,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,CAAA;QAC5E,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;QACtC,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;IAC7L,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,CAAA;IACpD,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,SAAS,CAAA;QACtD,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAA;QAClE,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;IAC7C,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAA;QAC3E,MAAM,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,EAAE,CAAA;QAChC,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC,CAAA;QAC1G,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,WAAW,IAAI,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,IAAI,EAAE,CAAC,CAAC,CAAA;IAClI,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;QACtC,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC,CAAA;QACjD,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAA;IACjD,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC7B,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC,CAAA;QAChD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAA;QACzD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,EAAE,CAAA;QAC5B,IAAI,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC,CAAA;QAC/E,OAAO,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,CAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACxF,CAAC;IACD,oDAAoD;IACpD,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,GAClB,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,uBAAuB,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAA;IAClI,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAA;QACzH,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,IAAI,EAAE,IAAI,IAAI,CAAC,GAAG,IAAI,EAAE,CAAA;QACnG,IAAI,QAAQ,IAAI,MAAM;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC,CAAA;QAC5C,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC5E,iDAAiD;YACjD,MAAM,KAAK,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;YAC/C,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACrF,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC,CAAA;IACtB,CAAC;IACD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAE,QAA8B,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;IACtH,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACtJ,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;AAC/C,CAAC;AACD,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAA;AAEtD,2FAA2F;AAE3F,+GAA+G;AAC/G,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;AAEnF,+CAA+C;AAC/C,MAAM,UAAU,OAAO,CAAC,MAAkC,EAAE,IAAY;IACtE,IAAI,IAAI,GAA+B,MAAM,CAAA;IAC7C,IAAI,KAAkC,CAAA;IACtC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;QACtC,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;QACvC,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAA;QAC5B,IAAI,IAAI,GAAmB,KAAK,CAAA;QAChC,OAAO,IAAI,CAAC,IAAI,KAAK,MAAM;YAAE,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;QAC7C,IAAI,GAAG,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;IAClD,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,qHAAqH;AACrH,SAAS,WAAW,CAAC,MAAyB,EAAE,IAAY;IAC1D,IAAI,CAAC,IAAI;QAAE,OAAO,MAAM,CAAA;IACxB,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACnC,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAA;IAC5B,IAAI,IAAI,GAAmB,KAAK,CAAA;IAChC,OAAO,IAAI,CAAC,IAAI,KAAK,MAAM;QAAE,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;IAC7C,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAA;AACzD,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,eAAe,CAAC,MAAkC,EAAE,KAAa,EAAE,KAAsB;IACvG,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAsB,CAAA;IACxD,WAAW,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;IACpC,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,oGAAoG;AACpG,MAAM,UAAU,uBAAuB,CAAC,MAAkC,EAAE,IAAY,EAAE,KAAsB;IAC9G,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAsB,CAAA;IACxD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAC7E,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;IACrC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;IACxD,MAAM,EAAE,GAAG,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IACtD,IAAI,IAAI,IAAI,EAAE,IAAI,CAAC;QAAE,IAAI,CAAC,EAAE,CAAC,GAAG,KAAK,CAAA;IACrC,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,kBAAkB,CAAC,MAAkC,EAAE,IAAY;IACjF,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAsB,CAAA;IACxD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAC7E,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;IACrC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;IACxD,MAAM,EAAE,GAAG,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IACtD,IAAI,IAAI,IAAI,EAAE,IAAI,CAAC;QAAE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;IACvC,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,WAAW,CAAC,KAAsB,EAAE,IAA4B;IAC9E,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAA;IACjF,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;IACpG,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAA;IACxH,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAqB,CAAA;AAC7C,CAAC;AAED,yFAAyF;AACzF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,KAAkB,EAAmB,EAAE,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;AAErI,uFAAuF;AAEvF;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAkB,EAAE,MAAe;IAC1D,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,KAAK,CAAC,CAAA;IACtC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IACpC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,CAAA;AAC/F,CAAC;AAED,2HAA2H;AAC3H,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAkB,EAAmB,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAA;AAEvK,sHAAsH;AACtH,MAAM,UAAU,YAAY,CAAC,IAAoB,EAAE,KAAa;IAC9D,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IACnF,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,KAAK,KAAK,MAAM,CAAA;IACpD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,KAAkB,EAAY,EAAE,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;AAE1G,yHAAyH;AACzH,MAAM,UAAU,WAAW,CAAC,KAAkB,EAAE,KAAa;IAC3D,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,aAAa,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3C,OAAO,EAAE,EAAE,EAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAA;AACxH,CAAC;AAED,sDAAsD;AACtD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAgB,EAAW,EAAE,CAAC,mBAAmB,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAA;AAElG;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAkB;IACpD,MAAM,GAAG,GAAsD,EAAE,CAAA;IACjE,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,aAAa,EAAE,QAAQ,IAAI,EAAE,EAAE,CAAC;QACpD,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,KAAK,aAAa,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,KAAK,cAAc;YAAE,SAAQ;QACrF,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;QACrE,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,KAAK,aAAa;YAAE,GAAG,CAAC,IAAI,GAAG,IAAI,CAAA;;YACjD,GAAG,CAAC,KAAK,GAAG,IAAI,CAAA;IACvB,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAWD,0FAA0F;AAC1F,MAAM,UAAU,eAAe,CAAC,KAAkB;IAChD,MAAM,GAAG,GAAmB,EAAE,CAAA;IAC9B,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;QACnC,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,CAAC,KAAK;YAAE,SAAQ;QAC1G,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC,EAAE,CAAA;QACjC,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,IAAI,CAAA;QACxE,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK;YAAE,SAAQ;QAC9C,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,IAAI,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IACrH,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAeD;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,KAAkB,EAAE,CAAiB;IACnE,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,CAAC,CAAA;IACnE,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,mBAAmB,CAAC,eAAe,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,KAAK,wCAAwC,CAAC,CAAA;IACvH,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,CAAA;IACrD,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC9E,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,KAAK,CAAC,KAAK,OAAO,CAAC,CAAA;IACxF,MAAM,OAAO,GAAG,KAAK,CAAA;IACrB,MAAM,OAAO,GAAG,GAAG,KAAK,UAAU,CAAA;IAClC,MAAM,KAAK,GAAyB,EAAE,CAAA;IACtC,MAAM,MAAM,GAAG,KAAK,CAAC,aAAa,EAAE,MAAM,IAAI,EAAE,CAAA;IAChD,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;QACZ,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAC3C,IAAI,KAAK,EAAE,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,GAAG,KAAK,SAAS,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,CAAC;YAClF,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,uBAAuB,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;IACpK,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,GAAW,EAAE,OAAgB,EAAsB,EAAE,CAAC,CAAC;QACjF,IAAI,EAAE,YAAY;QAClB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,IAAI;QACJ,QAAQ,EAAE,OAAO;QACjB,IAAI,EAAE;YACJ,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;YACxB,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI,EAAE;SAC/B;QACD,aAAa,EAAE,QAAQ;QACvB,IAAI,EAAE,MAAM;QACZ,WAAW,EAAE,IAAI;QACjB,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9B,CAAC,CAAA;IACF,MAAM,KAAK,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;IAC1D,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,CAAA;IACnC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;IAC5I,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;QAChB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAA;QACnC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;IACnI,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,aAAa,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAClE,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,OAAsC,EAAsB,EAAE;QACxF,MAAM,EAAE,GAAG,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QAChC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACd,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,EAAE,EAAE,CAAA;IAClE,CAAC,CAAA;IACD,IAAI,CAAC,CAAC,EAAE;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAA;IAC3J,IAAI,CAAC,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,QAAQ,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;IACvL,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;QAChB,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QAC1F,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,SAAU,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,SAAU,CAAC,OAAO,CAAC,CAAA;QAC3F,KAAK,CAAC,IAAI,CACR,IAAI,CAAC,GAAG,KAAK,YAAY,EAAE;YACzB,QAAQ,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI;YAC1B,QAAQ,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE;YACxC,IAAI,EAAE,KAAK;YACX,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE;SACpH,CAAC,CACH,CAAA;IACH,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC","sourcesContent":["/*\n * The figure's data and what it moves (ADR-0051 §8, data binding design appendix A): what the data tab offers and\n * shows. Every rule is figure-model's -- which accessor holds, which rule's results a behavior takes -- and the reading\n * of data is things-scene's; this only lists what there is to choose, drafts a binding the kernel then checks, and edits\n * the schema as a tree. Kept apart from the element so it is tested without a browser.\n */\nimport { FIGURE_PART_ID, FIGURE_MOTION_SPEED_MAX, FigureContractError, figureDataRows, parseFigureAccessor } from '@hatiolab/figure-model'\nimport type { FigureAsset, FigureDataBehavior, FigureDataBinding, FigureDataField, FigureDataNode, FigureDataRow, FigureDataValue, FigureDataValueIssue, FigureProposalStep, FigureStatusPort } from '@hatiolab/figure-model'\nimport { FigureInputs, type FigureDataResult } from '@hatiolab/things-scene'\n\n/** Something a binding can move, with what the screen shows of it. */\nexport interface BehaviorChoice {\n /** One string per behavior, for a select's value. */\n key: string\n behavior: FigureDataBehavior\n /** The screen's word for the kind: a control, a colour, a group's clip, … */\n kind: FigureDataBehavior['kind']\n /** The control's label, the colour token, the group's label, the port's label, the axis. */\n name: string\n unit?: string\n min?: number\n max?: number\n}\n\nexport const behaviorKeyOf = (b: FigureDataBehavior): string =>\n b.kind === 'state'\n ? `state:${b.input}`\n : b.kind === 'palette'\n ? `palette:${b.token}`\n : b.kind === 'status'\n ? `status:${b.port}`\n : b.kind === 'pose'\n ? `pose:${b.axis}`\n : b.kind === 'items'\n ? `items:${b.slot ?? ''}`\n : `${b.kind}:${b.group}`\n\n/**\n * Everything the figure can do that data may drive: each control, each colour, each motion group's clip, speed and\n * pause, each status port, its position and heading, and -- when it has places -- the items in them.\n */\nexport function behaviorsOf(asset: FigureAsset): BehaviorChoice[] {\n const out: BehaviorChoice[] = []\n const add = (behavior: FigureDataBehavior, name: string, more: Partial<BehaviorChoice> = {}) => out.push({ key: behaviorKeyOf(behavior), behavior, kind: behavior.kind, name, ...more })\n for (const input of asset.document.model.inputs) {\n if (input.role !== 'state') continue\n add({ kind: 'state', input: input.id, sourceMode: 'observed' }, asset.stateInputs?.[input.id]?.label ?? input.id, { unit: input.unit, min: input.min, max: input.max })\n }\n for (const token of Object.keys(asset.palette)) add({ kind: 'palette', token }, token)\n for (const group of asset.motionGroups ?? []) {\n add({ kind: 'motion-clip', group: group.id }, group.label)\n add({ kind: 'motion-speed', group: group.id }, group.label, { min: 0, max: FIGURE_MOTION_SPEED_MAX })\n add({ kind: 'motion-paused', group: group.id }, group.label)\n }\n for (const port of asset.dataInterface?.ports ?? []) add({ kind: 'status', port: port.id }, port.label)\n for (const axis of ['x', 'z', 'y', 'heading'] as const) add({ kind: 'pose', axis, seconds: 1 }, axis, { unit: axis === 'heading' ? 'deg' : 'mm' })\n if (asset.document.capabilities.slotLayout || asset.document.capabilities.names?.includes('Holdable')) add({ kind: 'items' }, asset.document.capabilities.slotLayout?.label ?? 'items')\n return out\n}\n\nconst isValue = (n: FigureDataNode): n is Extract<FigureDataNode, { kind: 'number' | 'boolean' | 'text' }> => n.kind === 'number' || n.kind === 'boolean' || n.kind === 'text'\n\n/** Whether a field of this kind can drive the behavior, by some rule -- the choices offered for it. */\nexport function fitsField(node: FigureDataNode, choice: BehaviorChoice): boolean {\n if (choice.kind === 'items') return node.kind === 'list' || node.kind === 'text' || node.kind === 'number'\n if (!isValue(node)) return false\n if (choice.kind === 'pose') return node.kind === 'number'\n if (choice.kind === 'motion-speed') return node.kind === 'number' || node.kind === 'boolean'\n if (choice.kind === 'motion-paused') return node.kind === 'boolean' || (node.kind === 'text' && !!node.values)\n if (choice.kind === 'status') return node.kind !== 'boolean'\n return true\n}\n\n/** An id not taken among `taken`: `base`, then `base-2`, `base-3`, … made into the kernel's id shape. */\nexport function freeIdOf(taken: Iterable<string>, base: string): string {\n const used = new Set(taken)\n const cleaned = base.replace(/[^a-zA-Z0-9_-]+/g, '-').replace(/^[^a-zA-Z]+/, '').replace(/-+$/, '') || 'binding'\n const id = FIGURE_PART_ID.test(cleaned) ? cleaned : 'binding'\n if (!used.has(id)) return id\n let n = 2\n while (used.has(`${id}-${n}`)) n++\n return `${id}-${n}`\n}\n\n/** How many mm, or degrees, one of a unit is: only lengths and angles are converted. */\nconst TO_MM: Record<string, number> = { mm: 1, cm: 10, m: 1000 }\nconst TO_DEG: Record<string, number> = { deg: 1, rad: 180 / Math.PI }\n\n/** The values a closed field takes: a closed text's values, both booleans. Empty when open. */\nexport const domainOf = (node: FigureDataNode): FigureDataValue[] => (node.kind === 'boolean' ? [false, true] : node.kind === 'text' && node.values ? [...node.values] : [])\n\n/**\n * A first binding of a field to a behavior, for the author to adjust: the value as it is when it already fits, a scale\n * when only its unit or range differs, one result per value (a map) for a closed field, a default for an open one.\n * Each result starts at what the behavior shows now -- the control's rest value, the colour, the group's default clip --\n * or, for a clip, the clip of the value's own name. The kernel says whether it holds.\n */\nexport function bindingDraftOf(asset: FigureAsset, accessor: string, node: FigureDataNode, choice: BehaviorChoice): FigureDataBinding {\n const taken = (asset.dataInterface?.bindings ?? []).map(b => b.id)\n const name = accessor.replace(/\\[\\d+\\]/g, '').split('.').pop() ?? accessor\n const id = freeIdOf(taken, `${name}-${choice.kind === 'state' ? (choice.behavior as { input: string }).input : choice.kind}`)\n const behavior = choice.behavior\n const make = (rule: FigureDataBinding['rule'], param?: FigureDataBinding['param']): FigureDataBinding => ({ id, accessor, behavior, rule, ...(param ? { param } : {}) })\n const domain = domainOf(node)\n const caseMap = (each: (v: FigureDataValue) => FigureDataValue, fallback: FigureDataValue) => Object.fromEntries([...domain.map(v => [String(v), each(v)] as const), ...(domain.length ? [] : [['default', fallback] as const])])\n\n if (behavior.kind === 'items') {\n if (node.kind !== 'list') return make('value')\n const fields = node.item.kind === 'object' ? node.item.fields : []\n const pick = (...words: RegExp[]) => fields.find(f => isValue(f) && words.some(w => w.test(f.name)))?.name\n const itemId = pick(/^id$/i, /name$/i, /id$/i) ?? fields.find(isValue)?.name\n const type = pick(/type$/i)\n const place = pick(/^slot/i, /place/i, /port/i)\n const stack = pick(/stack/i, /level/i)\n return { id, accessor, behavior: { kind: 'items', ...(itemId ? { id: itemId } : {}), ...(type ? { type } : {}), ...(place ? { place } : {}), ...(stack ? { stack } : {}) }, rule: 'value' }\n }\n if (behavior.kind === 'status') return make('value')\n if (behavior.kind === 'palette') {\n const now = asset.palette[behavior.token] ?? '#000000'\n if (node.kind === 'number') return make('range', { default: now })\n return make('map', caseMap(() => now, now))\n }\n if (behavior.kind === 'motion-clip') {\n const group = (asset.motionGroups ?? []).find(g => g.id === behavior.group)\n const clips = group?.clips ?? []\n if (node.kind === 'text' && node.values && node.values.every(v => clips.includes(v))) return make('value')\n return make('map', caseMap(v => (clips.includes(String(v)) ? String(v) : (group?.defaultClip ?? '')), group?.defaultClip ?? ''))\n }\n if (behavior.kind === 'motion-paused') {\n if (node.kind === 'boolean') return make('value')\n return make('map', caseMap(() => false, false))\n }\n if (behavior.kind === 'pose') {\n if (node.kind !== 'number') return make('value')\n const want = behavior.axis === 'heading' ? TO_DEG : TO_MM\n const unit = node.unit ?? ''\n if (unit === (behavior.axis === 'heading' ? 'deg' : 'mm')) return make('value')\n return unit in want ? make('scale', { scale: want[unit]!, offset: 0 }) : make('value')\n }\n /* state and motion-speed: a number into a range. */\n const [lo, hi, unit] =\n behavior.kind === 'motion-speed' ? [0, FIGURE_MOTION_SPEED_MAX, 'ratio'] : [choice.min ?? 0, choice.max ?? 1, choice.unit ?? '']\n if (node.kind === 'number') {\n const sameUnit = behavior.kind === 'motion-speed' ? node.unit === undefined || node.unit === 'ratio' : node.unit === unit\n const inside = node.min !== undefined && node.max !== undefined && node.min >= lo && node.max <= hi\n if (sameUnit && inside) return make('value')\n if (node.min !== undefined && node.max !== undefined && node.max > node.min) {\n /* The field's range laid onto the behavior's. */\n const scale = (hi - lo) / (node.max - node.min)\n return make('scale', { scale: round(scale), offset: round(lo - scale * node.min) })\n }\n return make('value')\n }\n const rest = behavior.kind === 'motion-speed' ? 1 : (asset.stateDefaults[(behavior as { input: string }).input] ?? lo)\n if (node.kind === 'boolean') return make('map', { false: behavior.kind === 'motion-speed' ? 0 : lo, true: behavior.kind === 'motion-speed' ? 1 : hi })\n return make('map', caseMap(() => rest, rest))\n}\nconst round = (v: number) => Math.round(v * 1e6) / 1e6\n\n/* ---------------------------------------------------------------- the schema as a tree */\n\n/** A row's path (`L[].F`) as the accessor a value binding reads (`L[0].F`): the first element of each list. */\nexport const accessorOfRow = (path: string): string => path.replace(/\\[\\]/g, '[0]')\n\n/** The field at a row's path, or undefined. */\nexport function fieldAt(schema: readonly FigureDataField[], path: string): FigureDataField | undefined {\n let list: readonly FigureDataField[] = schema\n let found: FigureDataField | undefined\n for (const part of path.split('.')) {\n const name = part.replace(/\\[\\]/g, '')\n found = list.find(f => f.name === name)\n if (!found) return undefined\n let node: FigureDataNode = found\n while (node.kind === 'list') node = node.item\n list = node.kind === 'object' ? node.fields : []\n }\n return found\n}\n\n/** The fields under a row's path, where a new field goes: the top, an object's fields, a list's element's fields. */\nfunction fieldsUnder(schema: FigureDataField[], path: string): FigureDataField[] | undefined {\n if (!path) return schema\n const field = fieldAt(schema, path)\n if (!field) return undefined\n let node: FigureDataNode = field\n while (node.kind === 'list') node = node.item\n return node.kind === 'object' ? node.fields : undefined\n}\n\n/** The schema with a field added under a path ('' for the top): a copy, the given one left as it is. */\nexport function schemaWithField(schema: readonly FigureDataField[], under: string, field: FigureDataField): FigureDataField[] {\n const out = structuredClone(schema) as FigureDataField[]\n fieldsUnder(out, under)?.push(field)\n return out\n}\n\n/** The schema with the field at a path replaced (its name kept unless the new one says another). */\nexport function schemaWithFieldReplaced(schema: readonly FigureDataField[], path: string, field: FigureDataField): FigureDataField[] {\n const out = structuredClone(schema) as FigureDataField[]\n const parent = path.includes('.') ? path.slice(0, path.lastIndexOf('.')) : ''\n const list = fieldsUnder(out, parent)\n const name = path.split('.').pop()!.replace(/\\[\\]/g, '')\n const at = list?.findIndex(f => f.name === name) ?? -1\n if (list && at >= 0) list[at] = field\n return out\n}\n\n/** The schema without the field at a path. */\nexport function schemaWithoutField(schema: readonly FigureDataField[], path: string): FigureDataField[] {\n const out = structuredClone(schema) as FigureDataField[]\n const parent = path.includes('.') ? path.slice(0, path.lastIndexOf('.')) : ''\n const list = fieldsUnder(out, parent)\n const name = path.split('.').pop()!.replace(/\\[\\]/g, '')\n const at = list?.findIndex(f => f.name === name) ?? -1\n if (list && at >= 0) list.splice(at, 1)\n return out\n}\n\n/** A field of another kind, its name, label and json flag kept, the rest started fresh. */\nexport function fieldOfKind(field: FigureDataField, kind: FigureDataNode['kind']): FigureDataField {\n const head = { name: field.name, ...(field.label ? { label: field.label } : {}) }\n if (kind === 'object') return { ...head, kind, fields: field.kind === 'object' ? field.fields : [] }\n if (kind === 'list') return { ...head, kind, item: field.kind === 'list' ? field.item : { kind: 'object', fields: [] } }\n return { ...head, kind } as FigureDataField\n}\n\n/** The rows of the schema, each with the bindings that read it (figure-model's view). */\nexport const schemaRowsOf = (asset: FigureAsset): FigureDataRow[] => (asset.dataInterface ? figureDataRows(asset.dataInterface) : [])\n\n/* ---------------------------------------------------------------- reading a sample */\n\n/**\n * What a sample of data would do on this figure, read the one way a placed figure reads it (things-scene's figure\n * inputs): what it moves, or why it is refused, each value's reason. Nothing here reads data itself.\n */\nexport function sampleOf(asset: FigureAsset, sample: unknown): { issues: FigureDataValueIssue[]; result: FigureDataResult } {\n const inputs = new FigureInputs(asset)\n const taken = inputs.setData(sample)\n return { issues: taken.accepted ? [] : (taken.issues[0]?.issues ?? []), result: inputs.data }\n}\n\n/** The rows a person gives a value by hand, as a board's saved values: one value, read by a binding, not inside a list. */\nexport const valueRowsOf = (asset: FigureAsset): FigureDataRow[] => schemaRowsOf(asset).filter(r => isValue(r.node) && r.bindings.length > 0 && !r.path.includes('[]'))\n\n/** A value typed or chosen for a row, as its kind reads it: a number (NaN when empty), true or false, or the text. */\nexport function typedValueOf(node: FigureDataNode, typed: string): FigureDataValue {\n if (node.kind === 'number') return typed.trim() === '' ? Number.NaN : Number(typed)\n if (node.kind === 'boolean') return typed === 'true'\n return typed\n}\n\n/** The parts a status port may show on: the parts the figure's appearance names. */\nexport const partsOf = (asset: FigureAsset): string[] => [...new Set(asset.appearance.map(a => a.target))]\n\n/** A first status port: the label as given, on the first colour token, no part yet. The kernel says whether it holds. */\nexport function portDraftOf(asset: FigureAsset, label: string): FigureStatusPort {\n const taken = (asset.dataInterface?.ports ?? []).map(p => p.id)\n const token = Object.keys(asset.palette)[0]\n return { id: freeIdOf(taken, label.trim() || 'status'), label: label.trim(), tokens: token ? [token] : [], parts: [] }\n}\n\n/** Whether a text is an accessor the kernel takes. */\nexport const accessorTaken = (accessor: string): boolean => parseFigureAccessor(accessor) !== null\n\n/**\n * Which fields of each motion group the data binds: its clip, its speed. A field bound wins over the group's own setting\n * whenever the data, a saved value or the schema's default gives it a value, so the preview drives it through the data.\n */\nexport function motionWrittenByData(asset: FigureAsset): Record<string, { clip: boolean; speed: boolean }> {\n const out: Record<string, { clip: boolean; speed: boolean }> = {}\n for (const b of asset.dataInterface?.bindings ?? []) {\n if (b.behavior.kind !== 'motion-clip' && b.behavior.kind !== 'motion-speed') continue\n const one = (out[b.behavior.group] ??= { clip: false, speed: false })\n if (b.behavior.kind === 'motion-clip') one.clip = true\n else one.speed = true\n }\n return out\n}\n\n/* ---------------------------------------------------------------- the rotation card */\n\n/** A joint that turns without end (a roller, a fan, a turntable): its control, by the id the clips write. */\nexport interface EndlessJoint {\n input: string\n label: string\n unit: 'deg' | 'rad'\n}\n\n/** The joints that turn without end: revolute, with no limits, leading no one's mimic. */\nexport function endlessJointsOf(asset: FigureAsset): EndlessJoint[] {\n const out: EndlessJoint[] = []\n for (const j of asset.joints ?? []) {\n if (j.type !== 'revolute' || j.lowerLimit !== undefined || j.upperLimit !== undefined || j.mimic) continue\n const input = j.state?.id ?? j.id\n const unit = asset.document.model.inputs.find(i => i.id === input)?.unit\n if (unit !== 'deg' && unit !== 'rad') continue\n if (!out.some(e => e.input === input)) out.push({ input, label: asset.stateInputs?.[input]?.label ?? input, unit })\n }\n return out\n}\n\n/** What the rotation card is told: the joint, and the data fields that turn it on, set its speed and its direction. */\nexport interface RotationChoice {\n input: string\n /** The motion group's name, shown to a person. */\n label: string\n /** A boolean field: true turns, false stops where it is. */\n on?: string\n /** A number field in turns per minute, and the most it gives; the field's range is set to 0..max when it has none. */\n speed?: { path: string; max: number }\n /** A closed text field, and its value that turns forward and the one that turns back. */\n direction?: { path: string; forward: string; reverse: string }\n}\n\n/**\n * The rotation card's commands (data binding design decision 1, 「회전」): a clip that turns the joint once around and\n * keeps adding the turn (wrap, accumulating), a reverse one when a direction is given, the motion group holding them,\n * and the bindings -- on to the group's pause, speed to its speed, direction to its clip. Nothing new runs them: they\n * are the drivers, the group and the bindings any author could write, and stay editable one by one.\n *\n * The clip lasts `T = 3 × 60 / max rpm` seconds, so the most the field gives plays at the group's top speed, 3 -- the\n * speed range is not widened. One turn per `T` at speed 1, and speed = rpm × T / 60. A stop is a pause: the joint stays\n * where it is, where a stopped clip would jump back to its first key.\n */\nexport function rotationStepsOf(asset: FigureAsset, c: RotationChoice): FigureProposalStep[] {\n const joint = endlessJointsOf(asset).find(j => j.input === c.input)\n if (!joint) throw new FigureContractError('TARGET_ABSENT', c.input, `${c.input} is not a joint that turns without end`)\n const turn = joint.unit === 'deg' ? 360 : 2 * Math.PI\n const seconds = c.speed && c.speed.max > 0 ? round((3 * 60) / c.speed.max) : 1\n const group = freeIdOf((asset.motionGroups ?? []).map(g => g.id), `${joint.input}-spin`)\n const forward = group\n const reverse = `${group}-reverse`\n const steps: FigureProposalStep[] = []\n const schema = asset.dataInterface?.schema ?? []\n if (c.speed) {\n const field = fieldAt(schema, c.speed.path)\n if (field?.kind === 'number' && (field.min === undefined || field.max === undefined))\n steps.push({ kind: 'set-data-schema', schema: schemaWithFieldReplaced(schema, c.speed.path, { ...field, min: field.min ?? 0, max: field.max ?? c.speed.max }) })\n }\n const spin = (clip: string, way: 1 | -1, inGroup: boolean): FigureProposalStep => ({\n kind: 'set-driver',\n state: joint.input,\n clip,\n duration: seconds,\n keys: [\n { seconds: 0, value: 0 },\n { seconds, value: way * turn }\n ],\n interpolation: 'linear',\n loop: 'wrap',\n accumulates: true,\n ...(inGroup ? { group } : {})\n })\n const clips = c.direction ? [forward, reverse] : [forward]\n steps.push(spin(forward, 1, false))\n steps.push({ kind: 'set-motion-group', group: { id: group, label: c.label, clips: [forward], defaultClip: forward, transitionSeconds: 0 } })\n if (c.direction) {\n steps.push(spin(reverse, -1, true))\n steps.push({ kind: 'set-motion-group', group: { id: group, label: c.label, clips, defaultClip: forward, transitionSeconds: 0 } })\n }\n const taken = (asset.dataInterface?.bindings ?? []).map(b => b.id)\n const bind = (base: string, binding: Omit<FigureDataBinding, 'id'>): FigureProposalStep => {\n const id = freeIdOf(taken, base)\n taken.push(id)\n return { kind: 'set-data-binding', binding: { id, ...binding } }\n }\n if (c.on) steps.push(bind(`${group}-on`, { accessor: c.on, behavior: { kind: 'motion-paused', group }, rule: 'map', param: { true: false, false: true } }))\n if (c.speed) steps.push(bind(`${group}-speed`, { accessor: c.speed.path, behavior: { kind: 'motion-speed', group }, rule: 'scale', param: { scale: round(seconds / 60), offset: 0 } }))\n if (c.direction) {\n const values = domainOf(fieldAt(schema, c.direction.path) ?? { kind: 'text' }).map(String)\n const covered = values.every(v => v === c.direction!.forward || v === c.direction!.reverse)\n steps.push(\n bind(`${group}-direction`, {\n accessor: c.direction.path,\n behavior: { kind: 'motion-clip', group },\n rule: 'map',\n param: { [c.direction.forward]: forward, [c.direction.reverse]: reverse, ...(covered ? {} : { default: forward }) }\n })\n )\n }\n return steps\n}\n"]}
@@ -194,7 +194,7 @@ export declare class FigureEditor extends LitElement {
194
194
  */
195
195
  private anchorsOf;
196
196
  private lastAnchors;
197
- /** Data values tried in the data tab, run by the preview's figure; null when none are (ADR-0051 §2.2). */
197
+ /** Data tried in the data tab, given to the preview's figures as their `data`; null when none is (ADR-0051 §8). */
198
198
  private dataSample;
199
199
  /** The figure's controls: try a value, name it, set its range and travel time, keep the pose. */
200
200
  private renderControls;
@@ -122,7 +122,7 @@ let FigureEditor = class FigureEditor extends LitElement {
122
122
  /** The preview is made when first asked for, and kept (hidden) after that; it is taken down with the editor. */
123
123
  this.previewMounted = false;
124
124
  this.lastAnchors = {};
125
- /** Data values tried in the data tab, run by the preview's figure; null when none are (ADR-0051 §2.2). */
125
+ /** Data tried in the data tab, given to the preview's figures as their `data`; null when none is (ADR-0051 §8). */
126
126
  this.dataSample = null;
127
127
  /** To the release that exists: the history tab's release section, not a second one here. */
128
128
  this.goRelease = async () => {
@@ -1693,8 +1693,9 @@ let FigureEditor = class FigureEditor extends LitElement {
1693
1693
  <figure-data-panel
1694
1694
  .asset=${asset}
1695
1695
  ?locked=${unavailable}
1696
- @data-sample=${(e) => (this.dataSample = e.detail.values)}
1696
+ @data-sample=${(e) => (this.dataSample = e.detail.data)}
1697
1697
  @part-edit=${(e) => this.run(() => this.session.edit(e.detail.edit))}
1698
+ @part-edits=${(e) => this.run(() => this.session.applyRecipe(e.detail.edits))}
1698
1699
  ></figure-data-panel>
1699
1700
  </div>
1700
1701
  <div panel flush ?off=${tab !== 'verdict'}><slot name="verdict"></slot></div>