@things-factory/figure-ui 10.1.47 → 10.1.48
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/client/modeller/figure-v3-parts.ts +1 -1
- package/client/modeller/v3-canvas.ts +37 -3
- package/client/modeller/v3-editor.ts +39 -4
- package/client/modeller/v3-fields.ts +22 -0
- package/client/modeller/v3-joint-fan.ts +292 -0
- package/client/modeller/v3-links.ts +8 -0
- package/client/modeller/v3-thumbnail.ts +8 -4
- package/dist-client/modeller/figure-v3-capability.d.ts +1 -1
- package/dist-client/modeller/figure-v3-clips.d.ts +1 -1
- package/dist-client/modeller/figure-v3-controls.d.ts +1 -1
- package/dist-client/modeller/figure-v3-links.d.ts +1 -1
- package/dist-client/modeller/figure-v3-material.d.ts +1 -1
- package/dist-client/modeller/figure-v3-parts.js +1 -1
- package/dist-client/modeller/figure-v3-parts.js.map +1 -1
- package/dist-client/modeller/figure-v3-properties.d.ts +1 -1
- package/dist-client/modeller/v3-canvas.d.ts +15 -0
- package/dist-client/modeller/v3-canvas.js +45 -3
- package/dist-client/modeller/v3-canvas.js.map +1 -1
- package/dist-client/modeller/v3-editor.d.ts +15 -0
- package/dist-client/modeller/v3-editor.js +42 -4
- package/dist-client/modeller/v3-editor.js.map +1 -1
- package/dist-client/modeller/v3-fields.d.ts +9 -0
- package/dist-client/modeller/v3-fields.js +23 -0
- package/dist-client/modeller/v3-fields.js.map +1 -1
- package/dist-client/modeller/v3-joint-fan.d.ts +9 -0
- package/dist-client/modeller/v3-joint-fan.js +264 -0
- package/dist-client/modeller/v3-joint-fan.js.map +1 -0
- package/dist-client/modeller/v3-links.d.ts +5 -0
- package/dist-client/modeller/v3-links.js +7 -0
- package/dist-client/modeller/v3-links.js.map +1 -1
- package/dist-client/modeller/v3-thumbnail.js +6 -3
- package/dist-client/modeller/v3-thumbnail.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/test/v3-fields.test.ts +10 -1
- package/test/v3-joint-fan.test.ts +210 -0
- package/test/v3-links.test.ts +13 -1
- package/test/v3-palette-icons.test.ts +22 -0
- package/test/v3-thumbnail.test.ts +29 -0
- package/translations/en.json +6 -1
- package/translations/ja.json +6 -1
- package/translations/ko.json +6 -1
- package/translations/ms.json +6 -1
- package/translations/zh.json +6 -1
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { compileV3Asset, compileV3Graph, v3WorldBoxesOf, v3WritersOf } from '@hatiolab/figure-model';
|
|
2
|
+
/*
|
|
3
|
+
* Where the selected part turns, and how far -- a translucent fan on the editing board (V2's joint gizmo, inventory
|
|
4
|
+
* 3.38). An axis name says nothing about which way a part swings; on a six-axis arm the author went to the preview and
|
|
5
|
+
* pushed each slider to its end to find out. The fan lies in the plane the part turns in, at the pivot, open from the
|
|
6
|
+
* control's lowest value to its highest; the edge at the value the figure holds now is where the part is.
|
|
7
|
+
*
|
|
8
|
+
* It is found by evaluating, not by reading the graph's shape: the frame that carries the part is sampled across the
|
|
9
|
+
* control's range, and a point at a fixed distance from the pivot traces the fan. So the direction is right whatever
|
|
10
|
+
* the slope of the control, and a joint inside other joints is drawn where its parents put it.
|
|
11
|
+
*
|
|
12
|
+
* Two ways a part turns are read:
|
|
13
|
+
* - a motion made in the modeller (`add-motion`): `<part>.motion`, the frame it moves is its seat, the pivot the
|
|
14
|
+
* seat's origin;
|
|
15
|
+
* - a joint the converter wrote from V2 (`<joint>.motion` · `<joint>.chain`): the part is composed on the joint's
|
|
16
|
+
* chain, the pivot is the joint's origin (`<joint>.frameOnParent`).
|
|
17
|
+
* A slide has no fan. Nothing is drawn when the turn cannot be found -- a fan in some guessed plane would point the
|
|
18
|
+
* wrong way.
|
|
19
|
+
*/
|
|
20
|
+
/** The fan's name on the board. It is not a part: not in the list, not saved, not picked. */
|
|
21
|
+
export const JOINT_FAN = '__joint-fan';
|
|
22
|
+
/** How thick the fan is: thin enough not to hide the part, thick enough to be seen edge-on. */
|
|
23
|
+
const THICKNESS = 6;
|
|
24
|
+
/** About one segment every six degrees, so a full turn reads as a circle. */
|
|
25
|
+
const STEP_DEG = 6;
|
|
26
|
+
const apply = (p, v) => [0, 1, 2].map(i => p.r[i][0] * v[0] + p.r[i][1] * v[1] + p.r[i][2] * v[2] + p.t[i]);
|
|
27
|
+
const sub = (a, b) => a.map((v, i) => v - b[i]);
|
|
28
|
+
const dot = (a, b) => a.reduce((s, v, i) => s + v * b[i], 0);
|
|
29
|
+
const cross = (a, b) => [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
|
|
30
|
+
const norm = (a) => {
|
|
31
|
+
const l = Math.hypot(...a);
|
|
32
|
+
return l > 1e-9 ? a.map(v => v / l) : null;
|
|
33
|
+
};
|
|
34
|
+
/** How far outside a part's box a pivot may lie and still be on the part: the kernel's contact skin. */
|
|
35
|
+
const SEAM = 1;
|
|
36
|
+
/** An origin this deep in a part's box (a share of its half-measure, on every axis) is housed by the part. */
|
|
37
|
+
const HOUSED = 0.5;
|
|
38
|
+
const AXIS_NAMES = ['x', 'y', 'z'];
|
|
39
|
+
function turnOf(asset, part, values, box) {
|
|
40
|
+
const m = asset.document.model;
|
|
41
|
+
const node = (id) => m.nodes.find(n => n.id === id);
|
|
42
|
+
const constant = (ref) => m.constants.find(c => c.id === ref)?.value;
|
|
43
|
+
const valueOf = (n) => (n ? values[Object.values(n.outputs)[0]] : undefined);
|
|
44
|
+
const controlOf = (ref, seen = new Set()) => {
|
|
45
|
+
if (!ref || seen.has(ref))
|
|
46
|
+
return null;
|
|
47
|
+
seen.add(ref);
|
|
48
|
+
const input = m.inputs.find(i => i.id === ref && i.role === 'state');
|
|
49
|
+
if (input)
|
|
50
|
+
return { id: input.id, min: input.min ?? 0, max: input.max ?? 0 };
|
|
51
|
+
const writer = m.nodes.find(n => Object.values(n.outputs).includes(ref));
|
|
52
|
+
for (const arg of writer?.args ?? []) {
|
|
53
|
+
const found = controlOf(arg, seen);
|
|
54
|
+
if (found)
|
|
55
|
+
return found;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
};
|
|
59
|
+
const axisOf = (motion) => {
|
|
60
|
+
const axis = motion.args.slice(0, 3).map(constant);
|
|
61
|
+
return axis.every(v => typeof v === 'number') ? norm(axis) : null;
|
|
62
|
+
};
|
|
63
|
+
/* Made in the modeller: the part's own motion turns its seat about the seat's origin. */
|
|
64
|
+
const own = node(`${part}.motion`);
|
|
65
|
+
if (own) {
|
|
66
|
+
if (own.op !== 'axis-turn@1')
|
|
67
|
+
return null;
|
|
68
|
+
const axis = axisOf(own);
|
|
69
|
+
const control = controlOf(own.args[3]);
|
|
70
|
+
const at = valueOf(node(`${part}.pose`));
|
|
71
|
+
if (!axis || !control || !at)
|
|
72
|
+
return null;
|
|
73
|
+
return { carrier: own.id, pivot: [0, 0, 0], axis, toward: at.t, control, turnedPart: part };
|
|
74
|
+
}
|
|
75
|
+
/*
|
|
76
|
+
Written by the converter: a joint (`<joint>.motion`, its origin `<joint>.frameOnParent`) carries its own part and
|
|
77
|
+
everything fastened below it, through its chain (the first joint of a chain has only its attachment). A part shows
|
|
78
|
+
the joint it is to that part of the mechanism (user, 2026-09-24: 「elbow-housing itself is a axis」 · 「please check
|
|
79
|
+
all that logic and concept」), tested in this order on the joints as they stand now:
|
|
80
|
+
1. housing -- a joint's origin lies deep inside the part, which is centred on it: the shoulder housing shows the
|
|
81
|
+
shoulder and the elbow housing the elbow, though another joint carries them;
|
|
82
|
+
2. turned -- the joint that carries the part, when its axis passes through the part: through its middle for a
|
|
83
|
+
body that rolls on it (the wrist roll, the tool flange, the gripper body), through its root for a link (the
|
|
84
|
+
upper arm, the forearm);
|
|
85
|
+
3. otherwise none: the pedestal (the base's axis runs through it but turns it not), the fingers.
|
|
86
|
+
An axis passing through a part is not enough by itself: the wrist roll's axis runs the forearm's length and the
|
|
87
|
+
tool roll's through the wrist pitch, and neither turns them (an earlier rule said so, and was wrong).
|
|
88
|
+
*/
|
|
89
|
+
if (!box)
|
|
90
|
+
return null;
|
|
91
|
+
const carrierOf = (joint) => (node(`${joint}.chain`) ? `${joint}.chain` : `${joint}.attach`);
|
|
92
|
+
const mine = node(`${part}.moved`)?.args.find(arg => /\.(chain|attach)\.value$/.test(arg))?.replace(/\.value$/, '');
|
|
93
|
+
const centre = AXIS_NAMES.map(a => box.centre[a]);
|
|
94
|
+
const half = AXIS_NAMES.map(a => box.extent[a] / 2);
|
|
95
|
+
const joints = m.nodes
|
|
96
|
+
.filter(n => n.op === 'axis-turn@1' && n.id.endsWith('.motion') && node(n.id.replace(/\.motion$/, '.frameOnParent')))
|
|
97
|
+
.map(motion => {
|
|
98
|
+
const joint = motion.id.replace(/\.motion$/, '');
|
|
99
|
+
const carrier = carrierOf(joint);
|
|
100
|
+
const frame = valueOf(node(carrier));
|
|
101
|
+
const origin = valueOf(node(`${joint}.frameOnParent`));
|
|
102
|
+
const local = axisOf(motion);
|
|
103
|
+
if (!frame || !origin || !local)
|
|
104
|
+
return null;
|
|
105
|
+
const at = apply(frame, origin.t);
|
|
106
|
+
const along = norm([0, 1, 2].map(i => dot(frame.r[i], local)));
|
|
107
|
+
if (!along)
|
|
108
|
+
return null;
|
|
109
|
+
const off = sub(centre, at);
|
|
110
|
+
return {
|
|
111
|
+
joint,
|
|
112
|
+
motion,
|
|
113
|
+
carrier,
|
|
114
|
+
origin,
|
|
115
|
+
/* How deep the origin lies: 0 at the centre, 1 on the box's face, on the axis where it is shallowest. */
|
|
116
|
+
depth: Math.max(...[0, 1, 2].map(i => Math.abs(off[i]) / (half[i] + SEAM))),
|
|
117
|
+
fromCentre: Math.hypot(...off),
|
|
118
|
+
through: lineMeetsBox(at, along, centre, half)
|
|
119
|
+
};
|
|
120
|
+
})
|
|
121
|
+
.filter((c) => !!c);
|
|
122
|
+
const housing = joints.filter(c => c.depth <= HOUSED).sort((a, b) => a.fromCentre - b.fromCentre)[0];
|
|
123
|
+
const carrying = joints.find(c => c.carrier === mine);
|
|
124
|
+
const chosen = housing ?? (carrying && carrying.through ? carrying : undefined);
|
|
125
|
+
if (!chosen)
|
|
126
|
+
return null;
|
|
127
|
+
const axis = axisOf(chosen.motion);
|
|
128
|
+
const control = controlOf(chosen.motion.args[3]);
|
|
129
|
+
if (!axis || !control)
|
|
130
|
+
return null;
|
|
131
|
+
/* The reference edge points at what the joint turns: of the parts it carries, the one farthest off its axis. */
|
|
132
|
+
const flatOff = (t) => {
|
|
133
|
+
const rel = sub(t, chosen.origin.t);
|
|
134
|
+
return Math.hypot(...sub(rel, axis.map(v => v * dot(rel, axis))));
|
|
135
|
+
};
|
|
136
|
+
const turned = m.nodes
|
|
137
|
+
.filter(n => n.id.endsWith('.moved') && n.args.includes(`${chosen.carrier}.value`))
|
|
138
|
+
.map(n => ({ id: n.id.replace(/\.moved$/, ''), t: valueOf(node(n.id.replace(/\.moved$/, '.pose')))?.t }))
|
|
139
|
+
.filter((one) => !!one.t)
|
|
140
|
+
.sort((a, b) => flatOff(b.t) - flatOff(a.t))[0];
|
|
141
|
+
return { carrier: chosen.carrier, pivot: chosen.origin.t, axis, toward: turned?.t ?? chosen.origin.t, control, turnedPart: turned?.id ?? part };
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* The fan for the part, as a body to stand on the board, or null when the part does not turn. `values` are the
|
|
145
|
+
* inputs the figure is drawn with now (a pose in hand included), so the fan follows the arm as it is posed.
|
|
146
|
+
*/
|
|
147
|
+
export function jointFanOf(asset, part, values = {}) {
|
|
148
|
+
if (!part)
|
|
149
|
+
return null;
|
|
150
|
+
let graph;
|
|
151
|
+
try {
|
|
152
|
+
graph = compileV3Graph(asset.document.model);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
const inputs = { ...asset.designInputs, ...asset.stateDefaults, ...values };
|
|
158
|
+
const at = (over) => graph.evaluate({ ...inputs, ...over }).values;
|
|
159
|
+
let now;
|
|
160
|
+
try {
|
|
161
|
+
now = at({});
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
let box;
|
|
167
|
+
try {
|
|
168
|
+
box = v3WorldBoxesOf(compileV3Asset({ ...asset, designInputs: { ...asset.designInputs, ...pick(values, asset.designInputs) }, stateDefaults: { ...asset.stateDefaults, ...pick(values, asset.stateDefaults) } })
|
|
169
|
+
.evaluate()
|
|
170
|
+
.geometry.filter(g => g.id === part), v3WritersOf(asset))[0];
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
box = undefined;
|
|
174
|
+
}
|
|
175
|
+
const turn = turnOf(asset, part, now, box);
|
|
176
|
+
if (!turn)
|
|
177
|
+
return null;
|
|
178
|
+
const carrierRef = Object.values(asset.document.model.nodes.find(n => n.id === turn.carrier).outputs)[0];
|
|
179
|
+
const carrierNow = now[carrierRef];
|
|
180
|
+
if (!carrierNow)
|
|
181
|
+
return null;
|
|
182
|
+
/* The reference edge points from the pivot toward the part, in the plane it turns in; any perpendicular if it sits on the axis. */
|
|
183
|
+
const rel = sub(turn.toward, turn.pivot);
|
|
184
|
+
const inPlane = sub(rel, turn.axis.map(v => v * dot(rel, turn.axis)));
|
|
185
|
+
const d = norm(inPlane) ?? norm(cross(turn.axis, Math.abs(turn.axis[0]) < 0.9 ? [1, 0, 0] : [0, 1, 0]));
|
|
186
|
+
/*
|
|
187
|
+
Sized from what the joint turns, as V2 sized it from the joint's child: selecting the elbow housing or the forearm
|
|
188
|
+
shows the elbow's one fan, not a smaller one for the smaller part (user, 2026-09-24).
|
|
189
|
+
*/
|
|
190
|
+
const radius = Math.max(60, Math.round(Math.max(largestMeasureOf(asset, turn.turnedPart, inputs), Math.hypot(...inPlane)) * 0.6));
|
|
191
|
+
const edge = turn.pivot.map((v, i) => v + d[i] * radius);
|
|
192
|
+
const { min, max } = turn.control;
|
|
193
|
+
if (!(max > min))
|
|
194
|
+
return null;
|
|
195
|
+
const steps = Math.max(2, Math.ceil((max - min) / STEP_DEG));
|
|
196
|
+
const pivot = apply(carrierNow, turn.pivot);
|
|
197
|
+
const traced = [];
|
|
198
|
+
try {
|
|
199
|
+
for (let i = 0; i <= steps; i++) {
|
|
200
|
+
const s = min + ((max - min) * i) / steps;
|
|
201
|
+
traced.push(apply(at({ [turn.control.id]: s })[carrierRef], edge));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
/* The fan's own frame: x toward the first traced point, y along the axis as it stands now, z completing them. */
|
|
208
|
+
const axisNow = norm([0, 1, 2].map(i => dot(carrierNow.r[i], turn.axis)));
|
|
209
|
+
const first = norm(sub(traced[0], pivot));
|
|
210
|
+
if (!axisNow || !first)
|
|
211
|
+
return null;
|
|
212
|
+
const zAxis = cross(first, axisNow);
|
|
213
|
+
const r = [0, 1, 2].map(i => [first[i], axisNow[i], zAxis[i]]);
|
|
214
|
+
const local = (p) => {
|
|
215
|
+
const v = sub(p, pivot);
|
|
216
|
+
return { x: dot(v, first), y: dot(v, zAxis) };
|
|
217
|
+
};
|
|
218
|
+
const full = max - min >= 359.999;
|
|
219
|
+
const profile = [...(full ? [] : [{ x: 0, y: 0 }]), ...traced.map(local)].map(p => ({ x: Math.round(p.x * 1000) / 1000, y: Math.round(p.y * 1000) / 1000 }));
|
|
220
|
+
return {
|
|
221
|
+
id: JOINT_FAN,
|
|
222
|
+
shape: { provider: 'polygon', dimensions: { height: THICKNESS }, profile },
|
|
223
|
+
pose: { t: pivot, r },
|
|
224
|
+
material: { token: '__joint-fan', transparent: true },
|
|
225
|
+
/** Which control turns it: the joint the fan shows. */
|
|
226
|
+
turns: turn.control.id
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
/** The values in hand that belong to a record's keys (design inputs, or state defaults). */
|
|
230
|
+
function pick(values, of) {
|
|
231
|
+
return Object.fromEntries(Object.entries(values).filter(([id]) => id in of));
|
|
232
|
+
}
|
|
233
|
+
/** A part's largest own measure (a box side, a diameter, a length) at the inputs given; 0 when it cannot be read. */
|
|
234
|
+
function largestMeasureOf(asset, part, inputs) {
|
|
235
|
+
try {
|
|
236
|
+
const body = compileV3Asset({ ...asset, designInputs: pick(inputs, asset.designInputs), stateDefaults: pick(inputs, asset.stateDefaults) })
|
|
237
|
+
.evaluate()
|
|
238
|
+
.geometry.find(g => g.id === part);
|
|
239
|
+
const dims = Object.entries(body?.shape?.dimensions ?? {}).map(([k, v]) => (k.startsWith('radius') ? v * 2 : v));
|
|
240
|
+
return dims.length ? Math.max(...dims) : 0;
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
return 0;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/** Whether the line through `at` along `along` passes through the box (centre, half-measures), within the skin. */
|
|
247
|
+
function lineMeetsBox(at, along, centre, half) {
|
|
248
|
+
let lo = -Infinity, hi = Infinity;
|
|
249
|
+
for (let i = 0; i < 3; i++) {
|
|
250
|
+
const min = centre[i] - half[i] - SEAM, max = centre[i] + half[i] + SEAM;
|
|
251
|
+
if (Math.abs(along[i]) < 1e-12) {
|
|
252
|
+
if (at[i] < min || at[i] > max)
|
|
253
|
+
return false;
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
const t1 = (min - at[i]) / along[i], t2 = (max - at[i]) / along[i];
|
|
257
|
+
lo = Math.max(lo, Math.min(t1, t2));
|
|
258
|
+
hi = Math.min(hi, Math.max(t1, t2));
|
|
259
|
+
if (lo > hi)
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=v3-joint-fan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v3-joint-fan.js","sourceRoot":"","sources":["../../client/modeller/v3-joint-fan.ts"],"names":[],"mappings":"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","sourcesContent":["import { compileV3Asset, compileV3Graph, v3WorldBoxesOf, v3WritersOf } from '@hatiolab/figure-model'\nimport type { V3Asset } from '@hatiolab/figure-model'\n\nimport type { V3RenderPart } from './v3-renderer.js'\n\n/*\n * Where the selected part turns, and how far -- a translucent fan on the editing board (V2's joint gizmo, inventory\n * 3.38). An axis name says nothing about which way a part swings; on a six-axis arm the author went to the preview and\n * pushed each slider to its end to find out. The fan lies in the plane the part turns in, at the pivot, open from the\n * control's lowest value to its highest; the edge at the value the figure holds now is where the part is.\n *\n * It is found by evaluating, not by reading the graph's shape: the frame that carries the part is sampled across the\n * control's range, and a point at a fixed distance from the pivot traces the fan. So the direction is right whatever\n * the slope of the control, and a joint inside other joints is drawn where its parents put it.\n *\n * Two ways a part turns are read:\n * - a motion made in the modeller (`add-motion`): `<part>.motion`, the frame it moves is its seat, the pivot the\n * seat's origin;\n * - a joint the converter wrote from V2 (`<joint>.motion` · `<joint>.chain`): the part is composed on the joint's\n * chain, the pivot is the joint's origin (`<joint>.frameOnParent`).\n * A slide has no fan. Nothing is drawn when the turn cannot be found -- a fan in some guessed plane would point the\n * wrong way.\n */\n\n/** The fan's name on the board. It is not a part: not in the list, not saved, not picked. */\nexport const JOINT_FAN = '__joint-fan'\n\n/** How thick the fan is: thin enough not to hide the part, thick enough to be seen edge-on. */\nconst THICKNESS = 6\n/** About one segment every six degrees, so a full turn reads as a circle. */\nconst STEP_DEG = 6\n\ntype Pose = { r: number[][]; t: number[] }\ninterface Node {\n id: string\n op: string\n args: string[]\n outputs: Record<string, string>\n params?: Record<string, string>\n}\n\nconst apply = (p: Pose, v: number[]) => [0, 1, 2].map(i => p.r[i]![0]! * v[0]! + p.r[i]![1]! * v[1]! + p.r[i]![2]! * v[2]! + p.t[i]!)\nconst sub = (a: number[], b: number[]) => a.map((v, i) => v - b[i]!)\nconst dot = (a: number[], b: number[]) => a.reduce((s, v, i) => s + v * b[i]!, 0)\nconst cross = (a: number[], b: number[]) => [a[1]! * b[2]! - a[2]! * b[1]!, a[2]! * b[0]! - a[0]! * b[2]!, a[0]! * b[1]! - a[1]! * b[0]!]\nconst norm = (a: number[]) => {\n const l = Math.hypot(...a)\n return l > 1e-9 ? a.map(v => v / l) : null\n}\n\n/** How far outside a part's box a pivot may lie and still be on the part: the kernel's contact skin. */\nconst SEAM = 1\n/** An origin this deep in a part's box (a share of its half-measure, on every axis) is housed by the part. */\nconst HOUSED = 0.5\nconst AXIS_NAMES = ['x', 'y', 'z'] as const\ntype WorldBox = { centre: Record<'x' | 'y' | 'z', number>; extent: Record<'x' | 'y' | 'z', number> }\n\ninterface Turn {\n /** The node whose value carries the moving frame into the figure's frame. */\n carrier: string\n /** The pivot, in the moving frame. */\n pivot: number[]\n /** The axis, in the moving frame. */\n axis: number[]\n /** Where the part sits, in the moving frame -- which way the fan's reference edge points. */\n toward: number[]\n /** The part the joint turns -- the fan is sized from it, so a joint shows one fan whichever part is selected. */\n turnedPart: string\n /** The state input that turns it. */\n control: { id: string; min: number; max: number }\n}\n\nfunction turnOf(asset: V3Asset, part: string, values: Record<string, unknown>, box: WorldBox | undefined): Turn | null {\n const m = asset.document.model as unknown as { nodes: Node[]; constants: { id: string; value: number }[]; inputs: { id: string; role?: string; min?: number; max?: number }[] }\n const node = (id: string) => m.nodes.find(n => n.id === id)\n const constant = (ref: string) => m.constants.find(c => c.id === ref)?.value\n const valueOf = (n: Node | undefined) => (n ? values[Object.values(n.outputs)[0]!] : undefined) as Pose | undefined\n const controlOf = (ref: string | undefined, seen = new Set<string>()): Turn['control'] | null => {\n if (!ref || seen.has(ref)) return null\n seen.add(ref)\n const input = m.inputs.find(i => i.id === ref && i.role === 'state')\n if (input) return { id: input.id, min: input.min ?? 0, max: input.max ?? 0 }\n const writer = m.nodes.find(n => Object.values(n.outputs).includes(ref))\n for (const arg of writer?.args ?? []) {\n const found = controlOf(arg, seen)\n if (found) return found\n }\n return null\n }\n const axisOf = (motion: Node) => {\n const axis = motion.args.slice(0, 3).map(constant)\n return axis.every(v => typeof v === 'number') ? norm(axis as number[]) : null\n }\n\n /* Made in the modeller: the part's own motion turns its seat about the seat's origin. */\n const own = node(`${part}.motion`)\n if (own) {\n if (own.op !== 'axis-turn@1') return null\n const axis = axisOf(own)\n const control = controlOf(own.args[3])\n const at = valueOf(node(`${part}.pose`))\n if (!axis || !control || !at) return null\n return { carrier: own.id, pivot: [0, 0, 0], axis, toward: at.t, control, turnedPart: part }\n }\n\n /*\n Written by the converter: a joint (`<joint>.motion`, its origin `<joint>.frameOnParent`) carries its own part and\n everything fastened below it, through its chain (the first joint of a chain has only its attachment). A part shows\n the joint it is to that part of the mechanism (user, 2026-09-24: 「elbow-housing itself is a axis」 · 「please check\n all that logic and concept」), tested in this order on the joints as they stand now:\n 1. housing -- a joint's origin lies deep inside the part, which is centred on it: the shoulder housing shows the\n shoulder and the elbow housing the elbow, though another joint carries them;\n 2. turned -- the joint that carries the part, when its axis passes through the part: through its middle for a\n body that rolls on it (the wrist roll, the tool flange, the gripper body), through its root for a link (the\n upper arm, the forearm);\n 3. otherwise none: the pedestal (the base's axis runs through it but turns it not), the fingers.\n An axis passing through a part is not enough by itself: the wrist roll's axis runs the forearm's length and the\n tool roll's through the wrist pitch, and neither turns them (an earlier rule said so, and was wrong).\n */\n if (!box) return null\n const carrierOf = (joint: string) => (node(`${joint}.chain`) ? `${joint}.chain` : `${joint}.attach`)\n const mine = node(`${part}.moved`)?.args.find(arg => /\\.(chain|attach)\\.value$/.test(arg))?.replace(/\\.value$/, '')\n const centre = AXIS_NAMES.map(a => box.centre[a])\n const half = AXIS_NAMES.map(a => box.extent[a] / 2)\n const joints = m.nodes\n .filter(n => n.op === 'axis-turn@1' && n.id.endsWith('.motion') && node(n.id.replace(/\\.motion$/, '.frameOnParent')))\n .map(motion => {\n const joint = motion.id.replace(/\\.motion$/, '')\n const carrier = carrierOf(joint)\n const frame = valueOf(node(carrier))\n const origin = valueOf(node(`${joint}.frameOnParent`))\n const local = axisOf(motion)\n if (!frame || !origin || !local) return null\n const at = apply(frame, origin.t)\n const along = norm([0, 1, 2].map(i => dot(frame.r[i]!, local)))\n if (!along) return null\n const off = sub(centre, at)\n return {\n joint,\n motion,\n carrier,\n origin,\n /* How deep the origin lies: 0 at the centre, 1 on the box's face, on the axis where it is shallowest. */\n depth: Math.max(...[0, 1, 2].map(i => Math.abs(off[i]!) / (half[i]! + SEAM))),\n fromCentre: Math.hypot(...off),\n through: lineMeetsBox(at, along, centre, half)\n }\n })\n .filter((c): c is NonNullable<typeof c> => !!c)\n const housing = joints.filter(c => c.depth <= HOUSED).sort((a, b) => a.fromCentre - b.fromCentre)[0]\n const carrying = joints.find(c => c.carrier === mine)\n const chosen = housing ?? (carrying && carrying.through ? carrying : undefined)\n if (!chosen) return null\n const axis = axisOf(chosen.motion)\n const control = controlOf(chosen.motion.args[3])\n if (!axis || !control) return null\n /* The reference edge points at what the joint turns: of the parts it carries, the one farthest off its axis. */\n const flatOff = (t: number[]) => {\n const rel = sub(t, chosen.origin.t)\n return Math.hypot(...sub(rel, axis.map(v => v * dot(rel, axis))))\n }\n const turned = m.nodes\n .filter(n => n.id.endsWith('.moved') && n.args.includes(`${chosen.carrier}.value`))\n .map(n => ({ id: n.id.replace(/\\.moved$/, ''), t: valueOf(node(n.id.replace(/\\.moved$/, '.pose')))?.t }))\n .filter((one): one is { id: string; t: number[] } => !!one.t)\n .sort((a, b) => flatOff(b.t) - flatOff(a.t))[0]\n return { carrier: chosen.carrier, pivot: chosen.origin.t, axis, toward: turned?.t ?? chosen.origin.t, control, turnedPart: turned?.id ?? part }\n}\n\n/**\n * The fan for the part, as a body to stand on the board, or null when the part does not turn. `values` are the\n * inputs the figure is drawn with now (a pose in hand included), so the fan follows the arm as it is posed.\n */\nexport function jointFanOf(asset: V3Asset, part: string, values: Record<string, number> = {}): V3RenderPart | null {\n if (!part) return null\n let graph: ReturnType<typeof compileV3Graph>\n try {\n graph = compileV3Graph(asset.document.model as never)\n } catch {\n return null\n }\n const inputs = { ...asset.designInputs, ...asset.stateDefaults, ...values }\n const at = (over: Record<string, number>) => graph.evaluate({ ...inputs, ...over }).values as Record<string, unknown>\n let now: Record<string, unknown>\n try {\n now = at({})\n } catch {\n return null\n }\n let box: WorldBox | undefined\n try {\n box = v3WorldBoxesOf(\n compileV3Asset({ ...asset, designInputs: { ...asset.designInputs, ...pick(values, asset.designInputs) }, stateDefaults: { ...asset.stateDefaults, ...pick(values, asset.stateDefaults) } })\n .evaluate()\n .geometry.filter(g => g.id === part),\n v3WritersOf(asset)\n )[0]\n } catch {\n box = undefined\n }\n const turn = turnOf(asset, part, now, box)\n if (!turn) return null\n const carrierRef = Object.values(asset.document.model.nodes.find(n => n.id === turn.carrier)!.outputs)[0]!\n const carrierNow = now[carrierRef] as Pose | undefined\n if (!carrierNow) return null\n\n /* The reference edge points from the pivot toward the part, in the plane it turns in; any perpendicular if it sits on the axis. */\n const rel = sub(turn.toward, turn.pivot)\n const inPlane = sub(rel, turn.axis.map(v => v * dot(rel, turn.axis)))\n const d = norm(inPlane) ?? norm(cross(turn.axis, Math.abs(turn.axis[0]!) < 0.9 ? [1, 0, 0] : [0, 1, 0]))!\n /*\n Sized from what the joint turns, as V2 sized it from the joint's child: selecting the elbow housing or the forearm\n shows the elbow's one fan, not a smaller one for the smaller part (user, 2026-09-24).\n */\n const radius = Math.max(60, Math.round(Math.max(largestMeasureOf(asset, turn.turnedPart, inputs), Math.hypot(...inPlane)) * 0.6))\n const edge = turn.pivot.map((v, i) => v + d[i]! * radius)\n\n const { min, max } = turn.control\n if (!(max > min)) return null\n const steps = Math.max(2, Math.ceil((max - min) / STEP_DEG))\n const pivot = apply(carrierNow, turn.pivot)\n const traced: number[][] = []\n try {\n for (let i = 0; i <= steps; i++) {\n const s = min + ((max - min) * i) / steps\n traced.push(apply(at({ [turn.control.id]: s })[carrierRef] as Pose, edge))\n }\n } catch {\n return null\n }\n\n /* The fan's own frame: x toward the first traced point, y along the axis as it stands now, z completing them. */\n const axisNow = norm([0, 1, 2].map(i => dot(carrierNow.r[i]!, turn.axis)))\n const first = norm(sub(traced[0]!, pivot))\n if (!axisNow || !first) return null\n const zAxis = cross(first, axisNow)\n const r = [0, 1, 2].map(i => [first[i]!, axisNow[i]!, zAxis[i]!])\n const local = (p: number[]) => {\n const v = sub(p, pivot)\n return { x: dot(v, first), y: dot(v, zAxis) }\n }\n const full = max - min >= 359.999\n const profile = [...(full ? [] : [{ x: 0, y: 0 }]), ...traced.map(local)].map(p => ({ x: Math.round(p.x * 1000) / 1000, y: Math.round(p.y * 1000) / 1000 }))\n\n return {\n id: JOINT_FAN,\n shape: { provider: 'polygon', dimensions: { height: THICKNESS }, profile },\n pose: { t: pivot, r },\n material: { token: '__joint-fan', transparent: true },\n /** Which control turns it: the joint the fan shows. */\n turns: turn.control.id\n } as unknown as V3RenderPart\n}\n\n/** The values in hand that belong to a record's keys (design inputs, or state defaults). */\nfunction pick(values: Record<string, number>, of: Record<string, number>): Record<string, number> {\n return Object.fromEntries(Object.entries(values).filter(([id]) => id in of))\n}\n\n/** A part's largest own measure (a box side, a diameter, a length) at the inputs given; 0 when it cannot be read. */\nfunction largestMeasureOf(asset: V3Asset, part: string, inputs: Record<string, number>): number {\n try {\n const body = compileV3Asset({ ...asset, designInputs: pick(inputs, asset.designInputs), stateDefaults: pick(inputs, asset.stateDefaults) })\n .evaluate()\n .geometry.find(g => g.id === part) as { shape?: { dimensions?: Record<string, number> } } | undefined\n const dims = Object.entries(body?.shape?.dimensions ?? {}).map(([k, v]) => (k.startsWith('radius') ? v * 2 : v))\n return dims.length ? Math.max(...dims) : 0\n } catch {\n return 0\n }\n}\n\n/** Whether the line through `at` along `along` passes through the box (centre, half-measures), within the skin. */\nfunction lineMeetsBox(at: number[], along: number[], centre: number[], half: number[]): boolean {\n let lo = -Infinity,\n hi = Infinity\n for (let i = 0; i < 3; i++) {\n const min = centre[i]! - half[i]! - SEAM,\n max = centre[i]! + half[i]! + SEAM\n if (Math.abs(along[i]!) < 1e-12) {\n if (at[i]! < min || at[i]! > max) return false\n continue\n }\n const t1 = (min - at[i]!) / along[i]!,\n t2 = (max - at[i]!) / along[i]!\n lo = Math.max(lo, Math.min(t1, t2))\n hi = Math.min(hi, Math.max(t1, t2))\n if (lo > hi) return false\n }\n return true\n}\n\n"]}
|
|
@@ -94,3 +94,8 @@ export declare function instanceRangeOf(asset: V3Asset): {
|
|
|
94
94
|
} | undefined;
|
|
95
95
|
/** Whether measuring again would change the declared range (to a micrometre). */
|
|
96
96
|
export declare function rangeDiffers(a: V3SizeRange, b: V3SizeRange): boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Whether the figure is a mechanism -- a part turns about a joint. Such a figure should grow by one ratio (chief
|
|
99
|
+
* architect's ruling 2026-09-24); the modeller proposes it, the author declares it.
|
|
100
|
+
*/
|
|
101
|
+
export declare function turnsAboutAJoint(asset: V3Asset): boolean;
|
|
@@ -224,4 +224,11 @@ export function instanceRangeOf(asset) {
|
|
|
224
224
|
export function rangeDiffers(a, b) {
|
|
225
225
|
return AXES.some(axis => Math.abs(a[axis].min - b[axis].min) > 1e-3 || Math.abs(a[axis].max - b[axis].max) > 1e-3);
|
|
226
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* Whether the figure is a mechanism -- a part turns about a joint. Such a figure should grow by one ratio (chief
|
|
229
|
+
* architect's ruling 2026-09-24); the modeller proposes it, the author declares it.
|
|
230
|
+
*/
|
|
231
|
+
export function turnsAboutAJoint(asset) {
|
|
232
|
+
return modelOf(asset).nodes.some(n => n.op === 'axis-turn@1');
|
|
233
|
+
}
|
|
227
234
|
//# sourceMappingURL=v3-links.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v3-links.js","sourceRoot":"","sources":["../../client/modeller/v3-links.ts"],"names":[],"mappings":"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","sourcesContent":["import { compileV3Graph, defaultInstanceSizeRange, foldInstanceSizeRange, v3AttachmentsOf, v3BindingsOf, v3InstanceSizeLimitsOf } from '@hatiolab/figure-model'\nimport type { V3SizeRange } from '@hatiolab/figure-model'\nimport type { V3Asset, V3BindingPlace, V3Face } from '@hatiolab/figure-model'\n\n/*\n * What a V3 part is tied to — the readers behind the parts' size response, fastening and motion sections (parity rows\n * ④ ⑤ ⑥). Kept apart from the element so they can be tested without a browser.\n */\n\nexport type Axis = 'x' | 'y' | 'z'\nexport const AXES: readonly Axis[] = ['x', 'y', 'z']\n\ninterface Node {\n id: string\n op: string\n args: string[]\n outputs: Record<string, string>\n}\nconst modelOf = (asset: V3Asset) =>\n asset.document.model as unknown as { nodes: Node[]; inputs: Array<{ id: string; role?: string; unit?: string; min?: number; max?: number }>; constants: Array<{ id: string; value: number }> }\n\n/** What one dimension of a part is worked out from. */\nexport type Follow =\n | { of: 'own' }\n /** `times` 1 is the instance size itself; any other is a share of it -- V2's proportional rule. */\n | { of: 'instance'; axis: Axis; times: number }\n | { of: 'part'; part: string; axis: Axis }\n | { of: 'input'; input: string }\n /** Worked out some other way (the converter writes expressions) — shown, not offered as a choice. */\n | { of: 'expression' }\n\n/**\n * `link-dimension` routes a part's dimension through `<part>.dimension.<axis>` and points its first argument at the\n * source, directly or through the nodes that scale it. Walking back through those nodes finds the source: the\n * instance size (`size.<axis>`), another part's dimension (`<part>.dimension.<axis>.value`) or a declared input.\n */\nexport function followOf(asset: V3Asset, part: string, axis: Axis): Follow {\n const m = modelOf(asset)\n /*\n A link routes the dimension through `<part>.dimension.<axis>`; before any link the shape reads its measure directly,\n and that may already be worked out -- the converter writes V2's rules there (ROBOT_ARM's flange filled what was left).\n */\n const node = m.nodes.find(n => n.id === `${part}.dimension.${axis}`)\n const start = node ? node.args[0] : m.nodes.find(n => n.id === `${part}.shape`)?.args[AXES.indexOf(axis)]\n if (!start || m.constants.some(c => c.id === start)) return { of: 'own' }\n const writerOf = (ref: string) => m.nodes.find(n => Object.values(n.outputs).includes(ref))\n const seen = new Set<string>()\n const constant = (ref: string) => m.constants.find(c => c.id === ref)\n /* The constant factors met on the way back: a share of the instance size is `size × share`. */\n let times = 1\n const walk = (ref: string): Follow => {\n if (seen.has(ref)) return { of: 'expression' }\n seen.add(ref)\n const instance = /^size\\.([xyz])$/.exec(ref)\n if (instance) return { of: 'instance', axis: instance[1] as Axis, times: Math.round(times * 1e6) / 1e6 }\n const other = /^(.+)\\.dimension\\.([xyz])\\.value$/.exec(ref)\n if (other) return { of: 'part', part: other[1]!, axis: other[2] as Axis }\n const input = m.inputs.find(i => i.id === ref)\n if (input) return ref.startsWith(`${part}.`) ? { of: 'own' } : { of: 'input', input: ref }\n const writer = writerOf(ref)\n if (!writer) return { of: 'expression' }\n /* Follow the one argument that is not a constant factor. */\n const onward = writer.args.filter(arg => !constant(arg))\n if (onward.length !== 1) return { of: 'expression' }\n /*\n Only a product is a share of its source. A sum with a number is not (V2's 「fill what is left」 is the size plus a\n negative gap -- ROBOT_ARM's tool flange), nor anything else; those read as worked out some other way.\n */\n const factors = writer.args.filter(arg => constant(arg)).map(arg => constant(arg)!.value)\n if (writer.op === 'mul@1') times *= factors.reduce((a, b) => a * b, 1)\n else if (writer.op === 'div@1' && writer.args[0] === onward[0] && factors.length === 1 && factors[0] !== 0) times /= factors[0]!\n else if (factors.some(v => v !== 0) || !['mul@1', 'div@1', 'add@1', 'sub@1'].includes(writer.op)) return { of: 'expression' }\n return walk(onward[0]!)\n }\n return walk(start)\n}\n\n/** Whether the asset declares an instance size on an axis — only then can a dimension follow it. */\nexport const hasInstanceSize = (asset: V3Asset, axis: Axis) => modelOf(asset).inputs.some(i => i.id === `size.${axis}` && i.role === 'design')\n\n/** The part this one is fastened to, or `null`. The mounting plane counts as fastened (the model names no part). */\nexport function fastenedTo(asset: V3Asset, part: string): string | null {\n return v3AttachmentsOf(asset)[part] ?? null\n}\n\n/** Whether a dimension of this part can follow something — box-shaped, whose three sizes are X, Y and Z. */\nexport function canFollow(asset: V3Asset, part: string): boolean {\n return followableAxes(asset, part).length > 0\n}\n\n/**\n * The axes whose size can follow something, by the shape's own measures: a box's X · Y · Z; a cylinder's radius (read\n * as X) and length (Y) -- its shape takes those two, so it has no Z of its own (V2's sizing cards had the cylinder's\n * two as well).\n */\nexport function followableAxes(asset: V3Asset, part: string): Axis[] {\n const shape = modelOf(asset).nodes.find(n => n.id === `${part}.shape`)\n if (!shape) return []\n if (shape.op === 'box-shape@1' || shape.op === 'rounded-box@1') return [...AXES]\n if (shape.op === 'cylinder-shape@1') return ['x', 'y']\n return []\n}\n\n/** What a part's own measure on an axis is now (a box's side, a cylinder's radius or length), at the design as it stands. */\nexport function dimensionNow(asset: V3Asset, part: string, axis: Axis): number | undefined {\n const m = modelOf(asset)\n const ref = m.nodes.find(n => n.id === `${part}.shape`)?.args[AXES.indexOf(axis)]\n if (!ref) return undefined\n try {\n const values = compileV3Graph(asset.document.model as never).evaluate({ ...asset.designInputs, ...asset.stateDefaults }).values as Record<string, number>\n const value = values[ref]\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined\n } catch {\n return undefined\n }\n}\n\n/** Whether `attach` can take this part: a box, or a cylinder (figure-model measures its faces since 0.1.47). */\nexport function canFasten(asset: V3Asset, part: string): boolean {\n const shape = modelOf(asset).nodes.find(n => n.id === `${part}.shape`)\n return canFollow(asset, part) || shape?.op === 'cylinder-shape@1'\n}\n\n/**\n * The axes on which the part's position is worked out from other values rather than being its own number — the\n * converter writes V2's anchor rules that way. `fix-position` makes one its own number where it is now.\n */\nexport function positionsWorkedOut(asset: V3Asset, part: string): Axis[] {\n const m = modelOf(asset)\n const pose = m.nodes.find(n => n.id === `${part}.pose`)\n if (!pose || pose.op !== 'rigid@1' || fastenedTo(asset, part)) return []\n const own = (ref: string) => ref.startsWith(`${part}.`) && m.inputs.some(i => i.id === ref && i.role === 'design')\n const constant = (ref: string) => m.constants.some(c => c.id === ref)\n return AXES.filter((_axis, i) => !own(pose.args[i]!) && !constant(pose.args[i]!))\n}\n\n/**\n * Whether the part's position on an axis is a share of the instance size (`scale-position`): `size.<axis> × share`.\n * Such a position is worked out too, but it already grows with the figure, so it is not offered again.\n */\nexport function positionIsShare(asset: V3Asset, part: string, axis: Axis): boolean {\n const m = modelOf(asset)\n const ref = m.nodes.find(n => n.id === `${part}.pose`)?.args[AXES.indexOf(axis)]\n const writer = ref ? m.nodes.find(n => Object.values(n.outputs).includes(ref)) : undefined\n return !!writer && writer.op === 'mul@1' && writer.args[0] === `size.${axis}` && m.constants.some(c => c.id === writer.args[1])\n}\n\n/** How a part moves, if it does: slide or turn, along which axis, driven by which control. */\nexport interface Motion {\n kind: 'slide' | 'turn'\n axis: Axis | '?'\n control?: { id: string; unit?: string; min?: number; max?: number }\n}\n\nexport function motionOf(asset: V3Asset, part: string): Motion | undefined {\n const m = modelOf(asset)\n const node = m.nodes.find(n => n.id === `${part}.motion`)\n if (!node) return undefined\n const kind = node.op === 'axis-turn@1' ? 'turn' : 'slide'\n const unit = node.args.slice(0, 3).map(ref => m.constants.find(c => c.id === ref)?.value)\n const axis = AXES[unit.findIndex(v => v !== undefined && v !== 0)] ?? '?'\n /* The quantity is the fourth argument, a state input directly or through the nodes that scale it. */\n const quantity = node.args[3]\n const seen = new Set<string>()\n const control = (ref?: string): Motion['control'] => {\n if (!ref || seen.has(ref)) return undefined\n seen.add(ref)\n const input = m.inputs.find(i => i.id === ref && i.role === 'state')\n if (input) return { id: input.id, unit: input.unit, min: input.min, max: input.max }\n const writer = m.nodes.find(n => Object.values(n.outputs).includes(ref))\n for (const arg of writer?.args ?? []) {\n const found = control(arg)\n if (found) return found\n }\n return undefined\n }\n return { kind, axis, control: control(quantity) }\n}\n\n/** The select's value for what a dimension follows now. */\nexport function choiceOf(follow: Follow): string {\n switch (follow.of) {\n case 'own':\n return 'own'\n case 'instance':\n return follow.times === 1 ? `instance:${follow.axis}` : `share:${follow.axis}`\n case 'part':\n return `part:${follow.part}:${follow.axis}`\n case 'input':\n return `input:${follow.input}`\n case 'expression':\n return 'expression'\n }\n}\n\n/** The faces a place can sit on for this part: a box's six, a rounded box's top and bottom, none for a round body. */\nexport function placeFacesOf(asset: V3Asset, part: string): V3Face[] {\n const shape = modelOf(asset).nodes.find(n => n.id === `${part}.shape`)\n if (shape?.op === 'box-shape@1') return ['left', 'right', 'bottom', 'top', 'back', 'front']\n if (shape?.op === 'rounded-box@1') return ['bottom', 'top']\n return []\n}\n\n/** The places things are held that sit on this part. */\nexport function placesOn(asset: V3Asset, part: string): V3BindingPlace[] {\n return v3BindingsOf(asset).filter(one => one.part === part)\n}\n\n/** An id for a new place on this part, not yet taken: `<part>-<role>`, then `-2`, `-3`. */\nexport function placeIdFor(asset: V3Asset, part: string, role: 'seat' | 'grip'): string {\n const taken = new Set(asset.document.capabilities.bindings.map(b => b.id))\n const base = `${part}-${role}`\n if (!taken.has(base)) return base\n let n = 2\n while (taken.has(`${base}-${n}`)) n++\n return `${base}-${n}`\n}\n\n/**\n * Whether a board will hand this figure anything: a place is a slot only on a figure that declares it holds things\n * (`Capacity`, or `FlowNode`, which includes it). Without it the places are declared and nothing arrives.\n */\nexport const holdsThings = (asset: V3Asset) => (asset.document.capabilities.names ?? []).some(n => n === 'Capacity' || n === 'FlowNode')\n\n/** What `accepts` a person typed: names split on commas, trimmed, blanks and repeats dropped. */\nexport const acceptsOf = (typed: string) => [...new Set(typed.split(',').map(one => one.trim()).filter(Boolean))]\n\n/**\n * The sizes a board may give this figure: the range it declares now, and the range measured afresh -- the default\n * (1 mm to ten times the authored size) narrowed only where the figure cannot follow, by the converter's own functions.\n * A range measured when a part filled what the others left (ROBOT_ARM: Y from 1,854 mm) stays narrow after the part is\n * made to grow with the figure; measuring again gives the figure its whole range back. Nothing when no size is declared.\n */\nexport function instanceRangeOf(asset: V3Asset): { declared: V3SizeRange; measured: V3SizeRange } | undefined {\n const inputs = modelOf(asset).inputs\n const declared = {} as V3SizeRange\n const authored = {} as Record<Axis, number>\n for (const axis of AXES) {\n const input = inputs.find(i => i.id === `size.${axis}` && i.role === 'design')\n const at = asset.designInputs[`size.${axis}`]\n if (!input || typeof at !== 'number') return undefined\n declared[axis] = { min: input.min ?? 0, max: input.max ?? Infinity }\n authored[axis] = at\n }\n const start = defaultInstanceSizeRange(authored)\n const measured = foldInstanceSizeRange(start, v3InstanceSizeLimitsOf(asset, start)).range\n return { declared, measured }\n}\n\n/** Whether measuring again would change the declared range (to a micrometre). */\nexport function rangeDiffers(a: V3SizeRange, b: V3SizeRange): boolean {\n return AXES.some(axis => Math.abs(a[axis].min - b[axis].min) > 1e-3 || Math.abs(a[axis].max - b[axis].max) > 1e-3)\n}\n\n"]}
|
|
1
|
+
{"version":3,"file":"v3-links.js","sourceRoot":"","sources":["../../client/modeller/v3-links.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,eAAe,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AAU/J,MAAM,CAAC,MAAM,IAAI,GAAoB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;AAQpD,MAAM,OAAO,GAAG,CAAC,KAAc,EAAE,EAAE,CACjC,KAAK,CAAC,QAAQ,CAAC,KAA+K,CAAA;AAYhM;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAc,EAAE,IAAY,EAAE,IAAU;IAC/D,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;IACxB;;;MAGE;IACF,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,cAAc,IAAI,EAAE,CAAC,CAAA;IACpE,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,QAAQ,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;IACzG,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAA;IACzE,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAA;IAC3F,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAA;IACrE,+FAA+F;IAC/F,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,MAAM,IAAI,GAAG,CAAC,GAAW,EAAU,EAAE;QACnC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,CAAA;QAC9C,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACb,MAAM,QAAQ,GAAG,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAC5C,IAAI,QAAQ;YAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAS,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,EAAE,CAAA;QACxG,MAAM,KAAK,GAAG,mCAAmC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAC3D,IAAI,KAAK;YAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAE,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAS,EAAE,CAAA;QACzE,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAA;QAC9C,IAAI,KAAK;YAAE,OAAO,GAAG,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAA;QAC1F,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAA;QAC5B,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,CAAA;QACxC,4DAA4D;QAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAA;QACxD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,CAAA;QACpD;;;UAGE;QACF,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAE,CAAC,KAAK,CAAC,CAAA;QACzF,IAAI,MAAM,CAAC,EAAE,KAAK,OAAO;YAAE,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;aACjE,IAAI,MAAM,CAAC,EAAE,KAAK,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;YAAE,KAAK,IAAI,OAAO,CAAC,CAAC,CAAE,CAAA;aAC3H,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,CAAA;QAC7H,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,CAAA;IACzB,CAAC,CAAA;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAA;AACpB,CAAC;AAED,oGAAoG;AACpG,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,KAAc,EAAE,IAAU,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAA;AAE9I,oHAAoH;AACpH,MAAM,UAAU,UAAU,CAAC,KAAc,EAAE,IAAY;IACrD,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,CAAA;AAC7C,CAAC;AAED,4GAA4G;AAC5G,MAAM,UAAU,SAAS,CAAC,KAAc,EAAE,IAAY;IACpD,OAAO,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAA;AAC/C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,IAAY;IACzD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,QAAQ,CAAC,CAAA;IACtE,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAA;IACrB,IAAI,KAAK,CAAC,EAAE,KAAK,aAAa,IAAI,KAAK,CAAC,EAAE,KAAK,eAAe;QAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA;IAChF,IAAI,KAAK,CAAC,EAAE,KAAK,kBAAkB;QAAE,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;IACtD,OAAO,EAAE,CAAA;AACX,CAAC;AAED,6HAA6H;AAC7H,MAAM,UAAU,YAAY,CAAC,KAAc,EAAE,IAAY,EAAE,IAAU;IACnE,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;IACxB,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,QAAQ,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;IACjF,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAA;IAC1B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAc,CAAC,CAAC,QAAQ,CAAC,EAAE,GAAG,KAAK,CAAC,YAAY,EAAE,GAAG,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,MAAgC,CAAA;QACzJ,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;QACzB,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;IAChF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED,gHAAgH;AAChH,MAAM,UAAU,SAAS,CAAC,KAAc,EAAE,IAAY;IACpD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,QAAQ,CAAC,CAAA;IACtE,OAAO,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,EAAE,EAAE,KAAK,kBAAkB,CAAA;AACnE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAc,EAAE,IAAY;IAC7D,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;IACxB,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,OAAO,CAAC,CAAA;IACvD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,KAAK,SAAS,IAAI,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC;QAAE,OAAO,EAAE,CAAA;IACxE,MAAM,GAAG,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAA;IAClH,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAA;IACrE,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC,CAAA;AACnF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc,EAAE,IAAY,EAAE,IAAU;IACtE,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;IACxB,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,OAAO,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;IAChF,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAC1F,OAAO,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,EAAE,KAAK,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,EAAE,IAAI,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;AACjI,CAAC;AASD,MAAM,UAAU,QAAQ,CAAC,KAAc,EAAE,IAAY;IACnD,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;IACxB,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,SAAS,CAAC,CAAA;IACzD,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAA;IAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,KAAK,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAA;IACzD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,CAAC,CAAA;IACzF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,CAAA;IACzE,qGAAqG;IACrG,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAC7B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,MAAM,OAAO,GAAG,CAAC,GAAY,EAAqB,EAAE;QAClD,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,SAAS,CAAA;QAC3C,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACb,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAA;QACpE,IAAI,KAAK;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAA;QACpF,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAA;QACxE,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC;YACrC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;YAC1B,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAA;QACzB,CAAC;QACD,OAAO,SAAS,CAAA;IAClB,CAAC,CAAA;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAA;AACnD,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,QAAQ,CAAC,MAAc;IACrC,QAAQ,MAAM,CAAC,EAAE,EAAE,CAAC;QAClB,KAAK,KAAK;YACR,OAAO,KAAK,CAAA;QACd,KAAK,UAAU;YACb,OAAO,MAAM,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,MAAM,CAAC,IAAI,EAAE,CAAA;QAChF,KAAK,MAAM;YACT,OAAO,QAAQ,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAA;QAC7C,KAAK,OAAO;YACV,OAAO,SAAS,MAAM,CAAC,KAAK,EAAE,CAAA;QAChC,KAAK,YAAY;YACf,OAAO,YAAY,CAAA;IACvB,CAAC;AACH,CAAC;AAED,sHAAsH;AACtH,MAAM,UAAU,YAAY,CAAC,KAAc,EAAE,IAAY;IACvD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,QAAQ,CAAC,CAAA;IACtE,IAAI,KAAK,EAAE,EAAE,KAAK,aAAa;QAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;IAC3F,IAAI,KAAK,EAAE,EAAE,KAAK,eAAe;QAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;IAC3D,OAAO,EAAE,CAAA;AACX,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,QAAQ,CAAC,KAAc,EAAE,IAAY;IACnD,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;AAC7D,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,UAAU,CAAC,KAAc,EAAE,IAAY,EAAE,IAAqB;IAC5E,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IAC1E,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,IAAI,EAAE,CAAA;IAC9B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IACjC,IAAI,CAAC,GAAG,CAAC,CAAA;IACT,OAAO,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC;QAAE,CAAC,EAAE,CAAA;IACrC,OAAO,GAAG,IAAI,IAAI,CAAC,EAAE,CAAA;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAc,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,UAAU,IAAI,CAAC,KAAK,UAAU,CAAC,CAAA;AAExI,iGAAiG;AACjG,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;AAEjH;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,CAAA;IACpC,MAAM,QAAQ,GAAG,EAAiB,CAAA;IAClC,MAAM,QAAQ,GAAG,EAA0B,CAAA;IAC3C,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAA;QAC9E,MAAM,EAAE,GAAG,KAAK,CAAC,YAAY,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAA;QAC7C,IAAI,CAAC,KAAK,IAAI,OAAO,EAAE,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAA;QACtD,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,QAAQ,EAAE,CAAA;QACpE,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA;IACrB,CAAC;IACD,MAAM,KAAK,GAAG,wBAAwB,CAAC,QAAQ,CAAC,CAAA;IAChD,MAAM,QAAQ,GAAG,qBAAqB,CAAC,KAAK,EAAE,sBAAsB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAA;IACzF,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAA;AAC/B,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,YAAY,CAAC,CAAc,EAAE,CAAc;IACzD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAA;AACpH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,aAAa,CAAC,CAAA;AAC/D,CAAC","sourcesContent":["import { compileV3Graph, defaultInstanceSizeRange, foldInstanceSizeRange, v3AttachmentsOf, v3BindingsOf, v3InstanceSizeLimitsOf } from '@hatiolab/figure-model'\nimport type { V3SizeRange } from '@hatiolab/figure-model'\nimport type { V3Asset, V3BindingPlace, V3Face } from '@hatiolab/figure-model'\n\n/*\n * What a V3 part is tied to — the readers behind the parts' size response, fastening and motion sections (parity rows\n * ④ ⑤ ⑥). Kept apart from the element so they can be tested without a browser.\n */\n\nexport type Axis = 'x' | 'y' | 'z'\nexport const AXES: readonly Axis[] = ['x', 'y', 'z']\n\ninterface Node {\n id: string\n op: string\n args: string[]\n outputs: Record<string, string>\n}\nconst modelOf = (asset: V3Asset) =>\n asset.document.model as unknown as { nodes: Node[]; inputs: Array<{ id: string; role?: string; unit?: string; min?: number; max?: number }>; constants: Array<{ id: string; value: number }> }\n\n/** What one dimension of a part is worked out from. */\nexport type Follow =\n | { of: 'own' }\n /** `times` 1 is the instance size itself; any other is a share of it -- V2's proportional rule. */\n | { of: 'instance'; axis: Axis; times: number }\n | { of: 'part'; part: string; axis: Axis }\n | { of: 'input'; input: string }\n /** Worked out some other way (the converter writes expressions) — shown, not offered as a choice. */\n | { of: 'expression' }\n\n/**\n * `link-dimension` routes a part's dimension through `<part>.dimension.<axis>` and points its first argument at the\n * source, directly or through the nodes that scale it. Walking back through those nodes finds the source: the\n * instance size (`size.<axis>`), another part's dimension (`<part>.dimension.<axis>.value`) or a declared input.\n */\nexport function followOf(asset: V3Asset, part: string, axis: Axis): Follow {\n const m = modelOf(asset)\n /*\n A link routes the dimension through `<part>.dimension.<axis>`; before any link the shape reads its measure directly,\n and that may already be worked out -- the converter writes V2's rules there (ROBOT_ARM's flange filled what was left).\n */\n const node = m.nodes.find(n => n.id === `${part}.dimension.${axis}`)\n const start = node ? node.args[0] : m.nodes.find(n => n.id === `${part}.shape`)?.args[AXES.indexOf(axis)]\n if (!start || m.constants.some(c => c.id === start)) return { of: 'own' }\n const writerOf = (ref: string) => m.nodes.find(n => Object.values(n.outputs).includes(ref))\n const seen = new Set<string>()\n const constant = (ref: string) => m.constants.find(c => c.id === ref)\n /* The constant factors met on the way back: a share of the instance size is `size × share`. */\n let times = 1\n const walk = (ref: string): Follow => {\n if (seen.has(ref)) return { of: 'expression' }\n seen.add(ref)\n const instance = /^size\\.([xyz])$/.exec(ref)\n if (instance) return { of: 'instance', axis: instance[1] as Axis, times: Math.round(times * 1e6) / 1e6 }\n const other = /^(.+)\\.dimension\\.([xyz])\\.value$/.exec(ref)\n if (other) return { of: 'part', part: other[1]!, axis: other[2] as Axis }\n const input = m.inputs.find(i => i.id === ref)\n if (input) return ref.startsWith(`${part}.`) ? { of: 'own' } : { of: 'input', input: ref }\n const writer = writerOf(ref)\n if (!writer) return { of: 'expression' }\n /* Follow the one argument that is not a constant factor. */\n const onward = writer.args.filter(arg => !constant(arg))\n if (onward.length !== 1) return { of: 'expression' }\n /*\n Only a product is a share of its source. A sum with a number is not (V2's 「fill what is left」 is the size plus a\n negative gap -- ROBOT_ARM's tool flange), nor anything else; those read as worked out some other way.\n */\n const factors = writer.args.filter(arg => constant(arg)).map(arg => constant(arg)!.value)\n if (writer.op === 'mul@1') times *= factors.reduce((a, b) => a * b, 1)\n else if (writer.op === 'div@1' && writer.args[0] === onward[0] && factors.length === 1 && factors[0] !== 0) times /= factors[0]!\n else if (factors.some(v => v !== 0) || !['mul@1', 'div@1', 'add@1', 'sub@1'].includes(writer.op)) return { of: 'expression' }\n return walk(onward[0]!)\n }\n return walk(start)\n}\n\n/** Whether the asset declares an instance size on an axis — only then can a dimension follow it. */\nexport const hasInstanceSize = (asset: V3Asset, axis: Axis) => modelOf(asset).inputs.some(i => i.id === `size.${axis}` && i.role === 'design')\n\n/** The part this one is fastened to, or `null`. The mounting plane counts as fastened (the model names no part). */\nexport function fastenedTo(asset: V3Asset, part: string): string | null {\n return v3AttachmentsOf(asset)[part] ?? null\n}\n\n/** Whether a dimension of this part can follow something — box-shaped, whose three sizes are X, Y and Z. */\nexport function canFollow(asset: V3Asset, part: string): boolean {\n return followableAxes(asset, part).length > 0\n}\n\n/**\n * The axes whose size can follow something, by the shape's own measures: a box's X · Y · Z; a cylinder's radius (read\n * as X) and length (Y) -- its shape takes those two, so it has no Z of its own (V2's sizing cards had the cylinder's\n * two as well).\n */\nexport function followableAxes(asset: V3Asset, part: string): Axis[] {\n const shape = modelOf(asset).nodes.find(n => n.id === `${part}.shape`)\n if (!shape) return []\n if (shape.op === 'box-shape@1' || shape.op === 'rounded-box@1') return [...AXES]\n if (shape.op === 'cylinder-shape@1') return ['x', 'y']\n return []\n}\n\n/** What a part's own measure on an axis is now (a box's side, a cylinder's radius or length), at the design as it stands. */\nexport function dimensionNow(asset: V3Asset, part: string, axis: Axis): number | undefined {\n const m = modelOf(asset)\n const ref = m.nodes.find(n => n.id === `${part}.shape`)?.args[AXES.indexOf(axis)]\n if (!ref) return undefined\n try {\n const values = compileV3Graph(asset.document.model as never).evaluate({ ...asset.designInputs, ...asset.stateDefaults }).values as Record<string, number>\n const value = values[ref]\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined\n } catch {\n return undefined\n }\n}\n\n/** Whether `attach` can take this part: a box, or a cylinder (figure-model measures its faces since 0.1.47). */\nexport function canFasten(asset: V3Asset, part: string): boolean {\n const shape = modelOf(asset).nodes.find(n => n.id === `${part}.shape`)\n return canFollow(asset, part) || shape?.op === 'cylinder-shape@1'\n}\n\n/**\n * The axes on which the part's position is worked out from other values rather than being its own number — the\n * converter writes V2's anchor rules that way. `fix-position` makes one its own number where it is now.\n */\nexport function positionsWorkedOut(asset: V3Asset, part: string): Axis[] {\n const m = modelOf(asset)\n const pose = m.nodes.find(n => n.id === `${part}.pose`)\n if (!pose || pose.op !== 'rigid@1' || fastenedTo(asset, part)) return []\n const own = (ref: string) => ref.startsWith(`${part}.`) && m.inputs.some(i => i.id === ref && i.role === 'design')\n const constant = (ref: string) => m.constants.some(c => c.id === ref)\n return AXES.filter((_axis, i) => !own(pose.args[i]!) && !constant(pose.args[i]!))\n}\n\n/**\n * Whether the part's position on an axis is a share of the instance size (`scale-position`): `size.<axis> × share`.\n * Such a position is worked out too, but it already grows with the figure, so it is not offered again.\n */\nexport function positionIsShare(asset: V3Asset, part: string, axis: Axis): boolean {\n const m = modelOf(asset)\n const ref = m.nodes.find(n => n.id === `${part}.pose`)?.args[AXES.indexOf(axis)]\n const writer = ref ? m.nodes.find(n => Object.values(n.outputs).includes(ref)) : undefined\n return !!writer && writer.op === 'mul@1' && writer.args[0] === `size.${axis}` && m.constants.some(c => c.id === writer.args[1])\n}\n\n/** How a part moves, if it does: slide or turn, along which axis, driven by which control. */\nexport interface Motion {\n kind: 'slide' | 'turn'\n axis: Axis | '?'\n control?: { id: string; unit?: string; min?: number; max?: number }\n}\n\nexport function motionOf(asset: V3Asset, part: string): Motion | undefined {\n const m = modelOf(asset)\n const node = m.nodes.find(n => n.id === `${part}.motion`)\n if (!node) return undefined\n const kind = node.op === 'axis-turn@1' ? 'turn' : 'slide'\n const unit = node.args.slice(0, 3).map(ref => m.constants.find(c => c.id === ref)?.value)\n const axis = AXES[unit.findIndex(v => v !== undefined && v !== 0)] ?? '?'\n /* The quantity is the fourth argument, a state input directly or through the nodes that scale it. */\n const quantity = node.args[3]\n const seen = new Set<string>()\n const control = (ref?: string): Motion['control'] => {\n if (!ref || seen.has(ref)) return undefined\n seen.add(ref)\n const input = m.inputs.find(i => i.id === ref && i.role === 'state')\n if (input) return { id: input.id, unit: input.unit, min: input.min, max: input.max }\n const writer = m.nodes.find(n => Object.values(n.outputs).includes(ref))\n for (const arg of writer?.args ?? []) {\n const found = control(arg)\n if (found) return found\n }\n return undefined\n }\n return { kind, axis, control: control(quantity) }\n}\n\n/** The select's value for what a dimension follows now. */\nexport function choiceOf(follow: Follow): string {\n switch (follow.of) {\n case 'own':\n return 'own'\n case 'instance':\n return follow.times === 1 ? `instance:${follow.axis}` : `share:${follow.axis}`\n case 'part':\n return `part:${follow.part}:${follow.axis}`\n case 'input':\n return `input:${follow.input}`\n case 'expression':\n return 'expression'\n }\n}\n\n/** The faces a place can sit on for this part: a box's six, a rounded box's top and bottom, none for a round body. */\nexport function placeFacesOf(asset: V3Asset, part: string): V3Face[] {\n const shape = modelOf(asset).nodes.find(n => n.id === `${part}.shape`)\n if (shape?.op === 'box-shape@1') return ['left', 'right', 'bottom', 'top', 'back', 'front']\n if (shape?.op === 'rounded-box@1') return ['bottom', 'top']\n return []\n}\n\n/** The places things are held that sit on this part. */\nexport function placesOn(asset: V3Asset, part: string): V3BindingPlace[] {\n return v3BindingsOf(asset).filter(one => one.part === part)\n}\n\n/** An id for a new place on this part, not yet taken: `<part>-<role>`, then `-2`, `-3`. */\nexport function placeIdFor(asset: V3Asset, part: string, role: 'seat' | 'grip'): string {\n const taken = new Set(asset.document.capabilities.bindings.map(b => b.id))\n const base = `${part}-${role}`\n if (!taken.has(base)) return base\n let n = 2\n while (taken.has(`${base}-${n}`)) n++\n return `${base}-${n}`\n}\n\n/**\n * Whether a board will hand this figure anything: a place is a slot only on a figure that declares it holds things\n * (`Capacity`, or `FlowNode`, which includes it). Without it the places are declared and nothing arrives.\n */\nexport const holdsThings = (asset: V3Asset) => (asset.document.capabilities.names ?? []).some(n => n === 'Capacity' || n === 'FlowNode')\n\n/** What `accepts` a person typed: names split on commas, trimmed, blanks and repeats dropped. */\nexport const acceptsOf = (typed: string) => [...new Set(typed.split(',').map(one => one.trim()).filter(Boolean))]\n\n/**\n * The sizes a board may give this figure: the range it declares now, and the range measured afresh -- the default\n * (1 mm to ten times the authored size) narrowed only where the figure cannot follow, by the converter's own functions.\n * A range measured when a part filled what the others left (ROBOT_ARM: Y from 1,854 mm) stays narrow after the part is\n * made to grow with the figure; measuring again gives the figure its whole range back. Nothing when no size is declared.\n */\nexport function instanceRangeOf(asset: V3Asset): { declared: V3SizeRange; measured: V3SizeRange } | undefined {\n const inputs = modelOf(asset).inputs\n const declared = {} as V3SizeRange\n const authored = {} as Record<Axis, number>\n for (const axis of AXES) {\n const input = inputs.find(i => i.id === `size.${axis}` && i.role === 'design')\n const at = asset.designInputs[`size.${axis}`]\n if (!input || typeof at !== 'number') return undefined\n declared[axis] = { min: input.min ?? 0, max: input.max ?? Infinity }\n authored[axis] = at\n }\n const start = defaultInstanceSizeRange(authored)\n const measured = foldInstanceSizeRange(start, v3InstanceSizeLimitsOf(asset, start)).range\n return { declared, measured }\n}\n\n/** Whether measuring again would change the declared range (to a micrometre). */\nexport function rangeDiffers(a: V3SizeRange, b: V3SizeRange): boolean {\n return AXES.some(axis => Math.abs(a[axis].min - b[axis].min) > 1e-3 || Math.abs(a[axis].max - b[axis].max) > 1e-3)\n}\n\n/**\n * Whether the figure is a mechanism -- a part turns about a joint. Such a figure should grow by one ratio (chief\n * architect's ruling 2026-09-24); the modeller proposes it, the author declares it.\n */\nexport function turnsAboutAJoint(asset: V3Asset): boolean {\n return modelOf(asset).nodes.some(n => n.op === 'axis-turn@1')\n}\n\n"]}
|
|
@@ -8,8 +8,9 @@ import './v3-canvas.js';
|
|
|
8
8
|
*
|
|
9
9
|
* 저작면의 캔버스는 저작자가 돌려 둔 각도와 확대로 서 있다. 그것을 찍으면 카드마다 프레임이
|
|
10
10
|
* 달라지고, 방금 확대해 보던 나사 하나가 그 자산의 얼굴이 된다. 목록은 견주는 자리이므로 그림은
|
|
11
|
-
* **정해진 크기와 정해진 카메라**로
|
|
12
|
-
*
|
|
11
|
+
* **정해진 크기와 정해진 카메라**로 찍는다 — 새로 세운 캔버스(`viewOnly`)가 스스로 `fit()` 한 자리에서
|
|
12
|
+
* `snapshot()` 으로 찍는다. 캔버스가 씬 위로 옮겨 가면서 찍는 길이 빠졌고, 그동안 저장은 그림 없이 갔다
|
|
13
|
+
* (사용자 2026-09-24).
|
|
13
14
|
*
|
|
14
15
|
* ## 왜 서버가 아니라 브라우저인가
|
|
15
16
|
*
|
|
@@ -51,13 +52,15 @@ export async function captureV3Thumbnail(parts, palette) {
|
|
|
51
52
|
shooter.parts = parts;
|
|
52
53
|
shooter.palette = palette;
|
|
53
54
|
shooter.context = 'thumbnail';
|
|
55
|
+
shooter.viewOnly = true;
|
|
54
56
|
shooter.style.cssText = 'display: block; width: 100%; height: 100%;';
|
|
55
57
|
try {
|
|
56
58
|
host.appendChild(shooter);
|
|
57
59
|
const shot = await within((async () => {
|
|
58
60
|
await settle();
|
|
59
61
|
shooter.fit?.();
|
|
60
|
-
|
|
62
|
+
await settle();
|
|
63
|
+
return shooter.snapshot ? await shooter.snapshot(WIDTH, HEIGHT) : undefined;
|
|
61
64
|
})(), PATIENCE);
|
|
62
65
|
if (!shot)
|
|
63
66
|
console.warn(`[thumbnail] ${PATIENCE}ms 안에 못 찍었습니다 — 그림 없이 저장합니다`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v3-thumbnail.js","sourceRoot":"","sources":["../../client/modeller/v3-thumbnail.ts"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,CAAA;AAIvB
|
|
1
|
+
{"version":3,"file":"v3-thumbnail.js","sourceRoot":"","sources":["../../client/modeller/v3-thumbnail.ts"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,CAAA;AAIvB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,wDAAwD;AACxD,MAAM,KAAK,GAAG,GAAG,CAAA;AACjB,MAAM,MAAM,GAAG,GAAG,CAAA;AAElB,mDAAmD;AACnD,MAAM,QAAQ,GAAG,IAAI,CAAA;AAWrB,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAA;AAEnF,yDAAyD;AACzD,SAAS,MAAM;IACb,MAAM,MAAM,GAAG,IAAI,OAAO,CAAO,OAAO,CAAC,EAAE,CACzC,qBAAqB,CAAC,GAAG,EAAE,CAAC,qBAAqB,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,CACnF,CAAA;IACD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;AAC3C,CAAC;AAED,SAAS,MAAM,CAAI,IAAgB,EAAE,EAAU;IAC7C,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;AAC9D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAqB,EACrB,OAA+B;IAE/B,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,SAAS,CAAA;IAEnC;;;MAGE;IACF,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;IAC1C,IAAI,CAAC,YAAY,CAAC,2BAA2B,EAAE,EAAE,CAAC,CAAA;IAClD,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,mDAAmD,KAAK,eAAe,MAAM,2BAA2B,CAAA;IAC7H,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;IAE/B,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,kBAAkB,CAAY,CAAA;IACrE,OAAO,CAAC,KAAK,GAAG,KAAK,CAAA;IACrB,OAAO,CAAC,OAAO,GAAG,OAAO,CAAA;IACzB,OAAO,CAAC,OAAO,GAAG,WAAW,CAAA;IAC7B,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAA;IACvB,OAAO,CAAC,KAAK,CAAC,OAAO,GAAG,4CAA4C,CAAA;IAEpE,IAAI,CAAC;QACH,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAA;QACzB,MAAM,IAAI,GAAG,MAAM,MAAM,CACvB,CAAC,KAAK,IAAI,EAAE;YACV,MAAM,MAAM,EAAE,CAAA;YACd,OAAO,CAAC,GAAG,EAAE,EAAE,CAAA;YACf,MAAM,MAAM,EAAE,CAAA;YACd,OAAO,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QAC7E,CAAC,CAAC,EAAE,EACJ,QAAQ,CACT,CAAA;QACD,IAAI,CAAC,IAAI;YAAE,OAAO,CAAC,IAAI,CAAC,eAAe,QAAQ,6BAA6B,CAAC,CAAA;QAC7E,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,2BAA4B,CAAW,CAAC,OAAO,EAAE,CAAC,CAAA;QAC/D,OAAO,SAAS,CAAA;IAClB,CAAC;YAAS,CAAC;QACT,IAAI,CAAC,MAAM,EAAE,CAAA;IACf,CAAC;AACH,CAAC","sourcesContent":["import './v3-canvas.js'\n\nimport type { V3RenderPart } from './v3-renderer.js'\n\n/**\n * 카드에 쓸 그림을 찍는다 — **숨은 자리에 V3 캔버스를 하나 더 세워서.**\n *\n * V2 의 `figure-thumbnail.ts` 와 같은 규율이고, 까닭도 같다.\n *\n * ## 왜 보이는 캔버스를 찍지 않나\n *\n * 저작면의 캔버스는 저작자가 돌려 둔 각도와 확대로 서 있다. 그것을 찍으면 카드마다 프레임이\n * 달라지고, 방금 확대해 보던 나사 하나가 그 자산의 얼굴이 된다. 목록은 견주는 자리이므로 그림은\n * **정해진 크기와 정해진 카메라**로 찍는다 — 새로 세운 캔버스(`viewOnly`)가 스스로 `fit()` 한 자리에서\n * `snapshot()` 으로 찍는다. 캔버스가 씬 위로 옮겨 가면서 찍는 길이 빠졌고, 그동안 저장은 그림 없이 갔다\n * (사용자 2026-09-24).\n *\n * ## 왜 서버가 아니라 브라우저인가\n *\n * 백엔드에 GPU 가 없는 배포가 많아 헤드리스로 3D 를 찍는 것이 자주 실패한다. 도형을 그릴 GPU 는\n * 이미 보는 사람 손에 있다.\n *\n * ## 못 찍어도 저장은 간다\n *\n * 탭이 뒤로 가면 브라우저가 `requestAnimationFrame` 을 멈춘다. 그 위에 저장이 얹혀 있으면 저장이\n * 통째로 멈춘다 — 단추는 잠긴 채로, 아무 말도 없이. 그래서 시계와 경주시키고, 늦으면 그림 없이\n * 간다. 정본이 그림보다 중하고, 그림은 다음에 저작면을 열 때 다시 채운다.\n */\n\n/** 카드는 4:3 이다. V2 와 같은 크기 — 목록에 두 판이 섞여 있어도 카드가 고르다. */\nconst WIDTH = 480\nconst HEIGHT = 360\n\n/** 넘으면 그림 없이 저장한다. 빠르기가 아니라 저장이 멈추지 않는 것이 목적이다. */\nconst PATIENCE = 5000\n\ninterface Shooter extends HTMLElement {\n parts?: V3RenderPart[]\n palette?: Record<string, string>\n context?: string\n viewOnly?: boolean\n snapshot?: (width?: number, height?: number) => Promise<string | undefined>\n fit?: () => void\n}\n\nconst delay = (ms: number) => new Promise<void>(resolve => setTimeout(resolve, ms))\n\n/** 씬이 서고 카메라가 자리를 잡을 틈. rAF 가 멈춘 탭에서도 풀리도록 시계와 경주시킨다. */\nfunction settle(): Promise<void> {\n const frames = new Promise<void>(resolve =>\n requestAnimationFrame(() => requestAnimationFrame(() => setTimeout(resolve, 150)))\n )\n return Promise.race([frames, delay(600)])\n}\n\nfunction within<T>(work: Promise<T>, ms: number): Promise<T | undefined> {\n return Promise.race([work, delay(ms).then(() => undefined)])\n}\n\nexport async function captureV3Thumbnail(\n parts: V3RenderPart[],\n palette: Record<string, string>\n): Promise<string | undefined> {\n if (!parts.length) return undefined\n\n /*\n 화면 밖에 둔다. `display: none` 이면 캔버스가 크기를 못 얻어 아무것도 그리지 못한다.\n WebGL context 는 브라우저가 8~16개에서 끊으므로 찍은 뒤 반드시 버린다(아래 finally).\n */\n const host = document.createElement('div')\n host.setAttribute('figure-v3-thumbnail-stage', '')\n host.style.cssText = `position: fixed; left: -10000px; top: 0; width: ${WIDTH}px; height: ${HEIGHT}px; pointer-events: none;`\n document.body.appendChild(host)\n\n const shooter = document.createElement('figure-v3-canvas') as Shooter\n shooter.parts = parts\n shooter.palette = palette\n shooter.context = 'thumbnail'\n shooter.viewOnly = true\n shooter.style.cssText = 'display: block; width: 100%; height: 100%;'\n\n try {\n host.appendChild(shooter)\n const shot = await within(\n (async () => {\n await settle()\n shooter.fit?.()\n await settle()\n return shooter.snapshot ? await shooter.snapshot(WIDTH, HEIGHT) : undefined\n })(),\n PATIENCE\n )\n if (!shot) console.warn(`[thumbnail] ${PATIENCE}ms 안에 못 찍었습니다 — 그림 없이 저장합니다`)\n return shot\n } catch (e) {\n console.warn(`[thumbnail] 세우지 못했습니다 — ${(e as Error).message}`)\n return undefined\n } finally {\n host.remove()\n }\n}\n"]}
|