@hatiolab/figure-model 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/asset-types.d.ts +34 -3
- package/dist/asset-types.d.ts.map +1 -1
- package/dist/capabilities.js +28 -4
- package/dist/capabilities.js.map +1 -1
- package/dist/graph-types.d.ts +60 -1
- package/dist/graph-types.d.ts.map +1 -1
- package/dist/graph.d.ts.map +1 -1
- package/dist/graph.js +212 -21
- package/dist/graph.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/kernel-version.d.ts +1 -1
- package/dist/kernel-version.js +1 -1
- package/docs/assembly-constraints.md +3 -3
- package/docs/asset-persistence.md +8 -8
- package/docs/authoring-transactions.md +2 -2
- package/docs/capability-bindings.md +1 -1
- package/docs/common-kernel-experiment.md +2 -2
- package/docs/completeness-review-2026-10-04.md +150 -1
- package/docs/core-semantics.md +2 -2
- package/docs/cutover.md +4 -4
- package/docs/document-contract.md +3 -3
- package/docs/dryroom-profile-validation.md +1 -1
- package/docs/editor-integration.md +4 -4
- package/docs/evaluation-changes-design.md +304 -0
- package/docs/evaluation-performance-summary-2026-10-05.md +218 -0
- package/docs/full-conveyor-validation.md +8 -8
- package/docs/full-dryroom-validation.md +5 -5
- package/docs/full-forklift-validation.md +5 -5
- package/docs/full-robot-validation.md +4 -4
- package/docs/layout-system.md +2 -2
- package/docs/legacy-risk-audit-2026-09-23.md +14 -14
- package/docs/minimal-model.md +3 -3
- package/docs/openusd-review.md +14 -14
- package/docs/operator-contracts.md +4 -4
- package/docs/prototypes/README.md +2 -2
- package/docs/prototypes/asset.schema.json +1 -1
- package/docs/prototypes/document.schema.json +1 -1
- package/docs/prototypes/editor-browser.mjs +2 -2
- package/docs/prototypes/linear-comparison.py +1 -1
- package/docs/prototypes/render-dryroom.py +1 -1
- package/docs/prototypes/render-full-dryroom.mjs +12 -12
- package/docs/prototypes/runtime-browser.mjs +3 -3
- package/docs/research-review.md +1 -1
- package/docs/review-2026-09-22.md +19 -19
- package/docs/runtime-extraction.md +10 -10
- package/docs/sample-validation-expanded.md +5 -5
- package/docs/sample-validation.md +5 -5
- package/docs/shape-dimension-contract.md +1 -1
- package/docs/spatial-contracts.md +2 -2
- package/docs/status.md +1 -1
- package/docs/storage-integration.md +5 -5
- package/package.json +1 -1
package/dist/graph.js
CHANGED
|
@@ -1055,11 +1055,16 @@ function compileInternal(source) {
|
|
|
1055
1055
|
let analysis;
|
|
1056
1056
|
/* Every call, refused or not; a pose step's \`read\` and \`changed\` answer while it is still the last one. */
|
|
1057
1057
|
let calls = 0;
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1058
|
+
/* Whether the bodies are others than the last ones (a whole evaluation always is): the generation moves on. */
|
|
1059
|
+
const moved = (now, whole) => {
|
|
1060
|
+
const other = whole || now.length !== ids.length || now.some((id, i) => id !== ids[i]);
|
|
1061
|
+
if (other)
|
|
1061
1062
|
generation++;
|
|
1062
1063
|
ids = now;
|
|
1064
|
+
return other;
|
|
1065
|
+
};
|
|
1066
|
+
const evaluated = (result, whole) => {
|
|
1067
|
+
moved(result.geometry.map((g) => g.id), whole);
|
|
1063
1068
|
return result;
|
|
1064
1069
|
};
|
|
1065
1070
|
const evaluate = (input) => {
|
|
@@ -1070,10 +1075,104 @@ function compileInternal(source) {
|
|
|
1070
1075
|
kept = hold(run(input, undefined).kept);
|
|
1071
1076
|
return evaluated({ values: { ...kept.values }, geometry: kept.geometry.slice() }, true);
|
|
1072
1077
|
}
|
|
1073
|
-
const result = step(input, previous);
|
|
1078
|
+
const result = step(input, previous, true);
|
|
1074
1079
|
kept = previous;
|
|
1075
1080
|
return evaluated(result, false);
|
|
1076
1081
|
};
|
|
1082
|
+
/*
|
|
1083
|
+
An evaluation told as its changes (\`FigureGraphChanges\`, chosen with \`changes: true\`). It runs what an evaluation
|
|
1084
|
+
runs, in the same order; what a snapshot would publish waits for the next \`evaluate\` (\`Held.pending\`). Told: the
|
|
1085
|
+
bodies that ran, by slot -- every slot when the bodies are others than the last ones, with the new ids and keys.
|
|
1086
|
+
*/
|
|
1087
|
+
const changesOf = (input, why) => {
|
|
1088
|
+
const call = ++calls;
|
|
1089
|
+
const previous = kept;
|
|
1090
|
+
kept = undefined;
|
|
1091
|
+
/* What pose steps moved since the last evaluation: an evaluation drops their state, so it is taken first. */
|
|
1092
|
+
const posed = previous?.fast;
|
|
1093
|
+
let ran;
|
|
1094
|
+
if (!previous) {
|
|
1095
|
+
kept = hold(run(input, undefined).kept, false);
|
|
1096
|
+
ran = w().shapes;
|
|
1097
|
+
}
|
|
1098
|
+
else {
|
|
1099
|
+
ran = step(input, previous, false);
|
|
1100
|
+
kept = previous;
|
|
1101
|
+
}
|
|
1102
|
+
const held = kept;
|
|
1103
|
+
const now = [], keys = [], paths = [], indexes = [];
|
|
1104
|
+
const start = new Int32Array(w().nodes.length);
|
|
1105
|
+
for (const index of w().shapes) {
|
|
1106
|
+
start[index] = now.length;
|
|
1107
|
+
for (const b of held.bodies[index] ?? []) {
|
|
1108
|
+
now.push(b.id);
|
|
1109
|
+
keys.push(b.key);
|
|
1110
|
+
/* A row's copy keeps the path through its rows: a frozen copy, told with the structure only. */
|
|
1111
|
+
paths.push(b.path ? Object.freeze(b.path.slice()) : undefined);
|
|
1112
|
+
indexes.push(b.index);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
const structure = moved(now, !previous);
|
|
1116
|
+
const told = structure ? w().shapes : posed?.wrote.size ? [...new Set([...ran, ...posed.wrote])].sort((a, b) => a - b) : ran;
|
|
1117
|
+
let count = 0;
|
|
1118
|
+
for (const index of told)
|
|
1119
|
+
count += held.bodies[index]?.length ?? 0;
|
|
1120
|
+
const bodies = new Int32Array(count), poses = new Float64Array(count * 12), shapes = new Array(count);
|
|
1121
|
+
let k = 0;
|
|
1122
|
+
for (const index of told)
|
|
1123
|
+
(held.bodies[index] ?? []).forEach((b, j) => {
|
|
1124
|
+
bodies[k] = start[index] + j;
|
|
1125
|
+
shapes[k] = b.shape ?? null;
|
|
1126
|
+
const o = k * 12;
|
|
1127
|
+
/* A body with no pose (a scalar provider) has no numbers: NaN, never a pose made up for it. */
|
|
1128
|
+
if (b.pose) {
|
|
1129
|
+
const r = b.pose.r, t = b.pose.t;
|
|
1130
|
+
poses[o] = r[0][0];
|
|
1131
|
+
poses[o + 1] = r[0][1];
|
|
1132
|
+
poses[o + 2] = r[0][2];
|
|
1133
|
+
poses[o + 3] = r[1][0];
|
|
1134
|
+
poses[o + 4] = r[1][1];
|
|
1135
|
+
poses[o + 5] = r[1][2];
|
|
1136
|
+
poses[o + 6] = r[2][0];
|
|
1137
|
+
poses[o + 7] = r[2][1];
|
|
1138
|
+
poses[o + 8] = r[2][2];
|
|
1139
|
+
poses[o + 9] = t[0];
|
|
1140
|
+
poses[o + 10] = t[1];
|
|
1141
|
+
poses[o + 11] = t[2];
|
|
1142
|
+
}
|
|
1143
|
+
else
|
|
1144
|
+
poses.fill(NaN, o, o + 12);
|
|
1145
|
+
k++;
|
|
1146
|
+
});
|
|
1147
|
+
const epoch = held.epoch;
|
|
1148
|
+
const live = () => {
|
|
1149
|
+
if (calls !== call)
|
|
1150
|
+
fail('STEP_EXPIRED', 'advance', 'read a step\'s changes before the next call on its evaluator');
|
|
1151
|
+
};
|
|
1152
|
+
return {
|
|
1153
|
+
kind: 'changes',
|
|
1154
|
+
generation,
|
|
1155
|
+
why,
|
|
1156
|
+
...(structure ? { structure: { ids: now, keys, paths, indexes } } : {}),
|
|
1157
|
+
bodies,
|
|
1158
|
+
poses,
|
|
1159
|
+
shapes,
|
|
1160
|
+
/* A number or a flag as it is; anything else a frozen copy made for this read -- never the evaluator's own. */
|
|
1161
|
+
read: (ref) => {
|
|
1162
|
+
live();
|
|
1163
|
+
const s = w().slot.get(ref);
|
|
1164
|
+
if (s === undefined)
|
|
1165
|
+
fail('TARGET_ABSENT', ref, 'no such value');
|
|
1166
|
+
return publicOf(held.slots[s], new Map());
|
|
1167
|
+
},
|
|
1168
|
+
changed: (ref) => {
|
|
1169
|
+
live();
|
|
1170
|
+
const s = w().slot.get(ref);
|
|
1171
|
+
return s !== undefined && (structure || held.mark[s] === epoch || !!posed?.moved.has(s));
|
|
1172
|
+
}
|
|
1173
|
+
};
|
|
1174
|
+
};
|
|
1175
|
+
const w = () => walkOf();
|
|
1077
1176
|
return {
|
|
1078
1177
|
evaluate,
|
|
1079
1178
|
/**
|
|
@@ -1105,16 +1204,47 @@ function compileInternal(source) {
|
|
|
1105
1204
|
changed: (ref) => (live(), changed(ref))
|
|
1106
1205
|
};
|
|
1107
1206
|
}
|
|
1207
|
+
if (options.changes)
|
|
1208
|
+
return changesOf(input, step.why);
|
|
1108
1209
|
const result = evaluate(input);
|
|
1109
1210
|
return { kind: 'evaluation', result, generation, why: step.why };
|
|
1110
1211
|
}
|
|
1111
1212
|
const bound = !!kept;
|
|
1213
|
+
const why = { code: bound ? ordinary : 'NOT_BOUND' };
|
|
1214
|
+
if (options.changes)
|
|
1215
|
+
return changesOf(input, why);
|
|
1112
1216
|
const result = evaluate(input);
|
|
1113
|
-
return { kind: 'evaluation', result, generation, why
|
|
1217
|
+
return { kind: 'evaluation', result, generation, why };
|
|
1114
1218
|
},
|
|
1115
1219
|
/** Which inputs take the fast path for what reads this graph, and why the others do not. */
|
|
1116
1220
|
poseInputs() {
|
|
1117
1221
|
return poseAnalysisOf(options.reads).inputs.map(a => ({ ...a }));
|
|
1222
|
+
},
|
|
1223
|
+
/**
|
|
1224
|
+
* One value as the evaluator stands now -- after the last evaluation and the pose steps since -- without a
|
|
1225
|
+
* snapshot: a number or a flag as it is, anything else a frozen copy made for this read, the caller's to keep.
|
|
1226
|
+
* Refused (NOT_BOUND) before the first evaluation and after a refusal.
|
|
1227
|
+
*/
|
|
1228
|
+
value(ref) {
|
|
1229
|
+
if (!kept)
|
|
1230
|
+
fail('NOT_BOUND', ref, 'evaluate before reading a value');
|
|
1231
|
+
const s = w().slot.get(ref);
|
|
1232
|
+
if (s === undefined)
|
|
1233
|
+
fail('TARGET_ABSENT', ref, 'no such value');
|
|
1234
|
+
/* The pose steps' slots start as the evaluation's and move on with each step: they are the latest. */
|
|
1235
|
+
const fast = kept.fast;
|
|
1236
|
+
if (!fast)
|
|
1237
|
+
return publicOf(kept.slots[s], new Map());
|
|
1238
|
+
/*
|
|
1239
|
+
Except a row nothing reads, whose copies a pose step writes as poses alone (its slot stays the evaluation's):
|
|
1240
|
+
asked for, it is run from the steps' slots, as an evaluation would run it.
|
|
1241
|
+
*/
|
|
1242
|
+
const writer = w().nodes.find(node => node.outs.includes(s));
|
|
1243
|
+
if (writer && fast.wrote.has(writer.index) && !fast.moved.has(s)) {
|
|
1244
|
+
const out = writer.op.run(writer.args.map(a => fast.slots[a]), writer.n.id, writer.n);
|
|
1245
|
+
return publicOf(out[writer.outs.indexOf(s)], new Map());
|
|
1246
|
+
}
|
|
1247
|
+
return publicOf(fast.slots[s], new Map());
|
|
1118
1248
|
}
|
|
1119
1249
|
};
|
|
1120
1250
|
}
|
|
@@ -1182,11 +1312,32 @@ function compileInternal(source) {
|
|
|
1182
1312
|
* What an evaluator keeps between calls: its slots, its bodies by node, and the last result as a caller is given it --
|
|
1183
1313
|
* frozen copies of the slots' values and bodies (\`publicOf\`), shapes frozen where they are.
|
|
1184
1314
|
*/
|
|
1185
|
-
function hold(whole) {
|
|
1315
|
+
function hold(whole, publish = true) {
|
|
1186
1316
|
const w = walkOf();
|
|
1187
1317
|
const slots = w.keys.map(k => whole.values.get(k));
|
|
1188
1318
|
slots.forEach((v, s) => w.shapeSlot[s] && frozen(v));
|
|
1189
1319
|
const bodies = w.nodes.map(node => whole.bodies.get(node.n.id));
|
|
1320
|
+
/*
|
|
1321
|
+
An evaluator told as changes publishes nothing until a snapshot is asked for: everything is pending, and the record
|
|
1322
|
+
holds its keys in the order a snapshot gives them.
|
|
1323
|
+
*/
|
|
1324
|
+
if (!publish) {
|
|
1325
|
+
const values = {};
|
|
1326
|
+
for (const k of w.keys)
|
|
1327
|
+
values[k] = undefined;
|
|
1328
|
+
return {
|
|
1329
|
+
raw: m.inputs.map(p => whole.input[p.id]),
|
|
1330
|
+
slots,
|
|
1331
|
+
bodies,
|
|
1332
|
+
shown: bodies.map(() => undefined),
|
|
1333
|
+
values,
|
|
1334
|
+
geometry: [],
|
|
1335
|
+
mark: new Int32Array(w.keys.length),
|
|
1336
|
+
ran: new Int32Array(w.nodes.length),
|
|
1337
|
+
epoch: 0,
|
|
1338
|
+
pending: { nodes: new Set(w.shapes.filter(index => bodies[index])), slots: new Set(w.keys.keys()) }
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1190
1341
|
const made = new Map();
|
|
1191
1342
|
const shown = bodies.map(own => own?.map(b => publicOf(b, made)));
|
|
1192
1343
|
const values = {};
|
|
@@ -1204,7 +1355,8 @@ function compileInternal(source) {
|
|
|
1204
1355
|
geometry,
|
|
1205
1356
|
mark: new Int32Array(w.keys.length),
|
|
1206
1357
|
ran: new Int32Array(w.nodes.length),
|
|
1207
|
-
epoch: 0
|
|
1358
|
+
epoch: 0,
|
|
1359
|
+
pending: { nodes: new Set(), slots: new Set() }
|
|
1208
1360
|
};
|
|
1209
1361
|
}
|
|
1210
1362
|
/*
|
|
@@ -1280,7 +1432,7 @@ function compileInternal(source) {
|
|
|
1280
1432
|
start[index] = at;
|
|
1281
1433
|
at += held.bodies[index]?.length ?? 0;
|
|
1282
1434
|
}
|
|
1283
|
-
return { raw: held.raw.slice(), slots: held.slots.slice(), mark: new Int32Array(w.keys.length), epoch: 0, start, bodyCount: at, prefix: new Map() };
|
|
1435
|
+
return { raw: held.raw.slice(), slots: held.slots.slice(), mark: new Int32Array(w.keys.length), epoch: 0, start, bodyCount: at, prefix: new Map(), wrote: new Set(), moved: new Set() };
|
|
1284
1436
|
}
|
|
1285
1437
|
/*
|
|
1286
1438
|
A pose step (ADR-0108): every changed input takes the fast path, so only poses move. The reach of the changed inputs
|
|
@@ -1292,13 +1444,18 @@ function compileInternal(source) {
|
|
|
1292
1444
|
function poseStep(input, held, analysis) {
|
|
1293
1445
|
const w = walkOf();
|
|
1294
1446
|
inputFields(input);
|
|
1295
|
-
|
|
1447
|
+
/*
|
|
1448
|
+
What moved is told against the pose steps' own inputs, or the evaluation's while no pose step has been bound since:
|
|
1449
|
+
an input that takes no fast path is found before the steps' state is made (it was made, and dropped, at every
|
|
1450
|
+
evaluation frame of a resize -- 2026-10-05).
|
|
1451
|
+
*/
|
|
1452
|
+
const raw = held.fast?.raw ?? held.raw;
|
|
1296
1453
|
const moved = [];
|
|
1297
1454
|
const values = [];
|
|
1298
1455
|
for (let i = 0; i < m.inputs.length; i++) {
|
|
1299
1456
|
const p = m.inputs[i];
|
|
1300
1457
|
const given = input[p.id];
|
|
1301
|
-
if (given ===
|
|
1458
|
+
if (given === raw[i])
|
|
1302
1459
|
continue;
|
|
1303
1460
|
values.push(inputValue(p, given));
|
|
1304
1461
|
moved.push(i);
|
|
@@ -1306,6 +1463,7 @@ function compileInternal(source) {
|
|
|
1306
1463
|
for (const i of moved)
|
|
1307
1464
|
if (!analysis.inputs[i].fast)
|
|
1308
1465
|
return { why: analysis.inputs[i] };
|
|
1466
|
+
const fast = (held.fast ??= bindFast(held));
|
|
1309
1467
|
const epoch = ++fast.epoch;
|
|
1310
1468
|
moved.forEach((i, k) => {
|
|
1311
1469
|
fast.raw[i] = input[m.inputs[i].id];
|
|
@@ -1369,12 +1527,14 @@ function compileInternal(source) {
|
|
|
1369
1527
|
finite(p, node.n.id);
|
|
1370
1528
|
write(slot++, p);
|
|
1371
1529
|
}
|
|
1530
|
+
fast.wrote.add(index);
|
|
1372
1531
|
continue;
|
|
1373
1532
|
}
|
|
1374
1533
|
const out = node.op.run(args, node.n.id, node.n);
|
|
1375
1534
|
node.outs.forEach((s, i) => {
|
|
1376
1535
|
fast.slots[s] = checkedValue(out[i], node.types[i], node.n.id);
|
|
1377
1536
|
fast.mark[s] = epoch;
|
|
1537
|
+
fast.moved.add(s);
|
|
1378
1538
|
});
|
|
1379
1539
|
if (node.op.geometry) {
|
|
1380
1540
|
const g = node.op.geometry(args, out, node.n);
|
|
@@ -1382,6 +1542,7 @@ function compileInternal(source) {
|
|
|
1382
1542
|
let slot = fast.start[index];
|
|
1383
1543
|
for (const body of own)
|
|
1384
1544
|
write(slot++, body.pose);
|
|
1545
|
+
fast.wrote.add(index);
|
|
1385
1546
|
}
|
|
1386
1547
|
}
|
|
1387
1548
|
const result = {
|
|
@@ -1403,7 +1564,7 @@ function compileInternal(source) {
|
|
|
1403
1564
|
};
|
|
1404
1565
|
return { result };
|
|
1405
1566
|
}
|
|
1406
|
-
function step(input, held) {
|
|
1567
|
+
function step(input, held, publish) {
|
|
1407
1568
|
const w = walkOf();
|
|
1408
1569
|
inputFields(input);
|
|
1409
1570
|
held.fast = undefined;
|
|
@@ -1438,9 +1599,7 @@ function compileInternal(source) {
|
|
|
1438
1599
|
}
|
|
1439
1600
|
visit = all.sort((a, b) => a - b);
|
|
1440
1601
|
}
|
|
1441
|
-
|
|
1442
|
-
/* The copies made in this call, so a pose a body and a value both carry is copied once. */
|
|
1443
|
-
let made;
|
|
1602
|
+
const ran = [];
|
|
1444
1603
|
for (const index of visit) {
|
|
1445
1604
|
const node = w.nodes[index];
|
|
1446
1605
|
if (node.op.pure && !node.args.some(s => held.mark[s] === epoch))
|
|
@@ -1455,15 +1614,46 @@ function compileInternal(source) {
|
|
|
1455
1614
|
touched.push(s);
|
|
1456
1615
|
});
|
|
1457
1616
|
if (node.op.geometry) {
|
|
1458
|
-
|
|
1459
|
-
held.
|
|
1460
|
-
|
|
1461
|
-
reshaped = true;
|
|
1617
|
+
held.bodies[index] = bodiesOf(node.op.geometry(args, out, node.n), node.n.id);
|
|
1618
|
+
held.pending.nodes.add(index);
|
|
1619
|
+
ran.push(index);
|
|
1462
1620
|
}
|
|
1463
1621
|
}
|
|
1464
1622
|
for (const s of touched)
|
|
1465
|
-
held.
|
|
1466
|
-
if (
|
|
1623
|
+
held.pending.slots.add(s);
|
|
1624
|
+
if (!publish) {
|
|
1625
|
+
/*
|
|
1626
|
+
What ran is published again at the next snapshot: the copies made of it before are let go here, so a snapshot read
|
|
1627
|
+
once does not keep its changed bodies and values alive through the changes after it. A snapshot a caller kept is
|
|
1628
|
+
not touched; what did not run keeps its copies, the same objects in the next snapshot.
|
|
1629
|
+
*/
|
|
1630
|
+
for (const index of ran)
|
|
1631
|
+
held.shown[index] = undefined;
|
|
1632
|
+
if (ran.length)
|
|
1633
|
+
held.geometry = [];
|
|
1634
|
+
for (const s of touched)
|
|
1635
|
+
held.values[w.keys[s]] = undefined;
|
|
1636
|
+
return ran;
|
|
1637
|
+
}
|
|
1638
|
+
publishPending(held);
|
|
1639
|
+
return { values: { ...held.values }, geometry: held.geometry.slice() };
|
|
1640
|
+
}
|
|
1641
|
+
/*
|
|
1642
|
+
What a snapshot shows of what ran since the last one (\`Held.pending\`): frozen copies of the bodies and values made
|
|
1643
|
+
then, once each however many evaluations ran them, in the order a snapshot made them before -- bodies, then values,
|
|
1644
|
+
one copy of a pose both carry. What did not run keeps the copies the earlier snapshots hold.
|
|
1645
|
+
*/
|
|
1646
|
+
function publishPending(held) {
|
|
1647
|
+
const w = walkOf();
|
|
1648
|
+
const { nodes, slots } = held.pending;
|
|
1649
|
+
if (!nodes.size && !slots.size)
|
|
1650
|
+
return;
|
|
1651
|
+
const made = new Map();
|
|
1652
|
+
for (const index of [...nodes].sort((a, b) => a - b))
|
|
1653
|
+
held.shown[index] = held.bodies[index].map(b => publicOf(b, made));
|
|
1654
|
+
for (const s of slots)
|
|
1655
|
+
held.values[w.keys[s]] = publicOf(held.slots[s], made);
|
|
1656
|
+
if (nodes.size) {
|
|
1467
1657
|
const geometry = [];
|
|
1468
1658
|
for (const index of w.shapes) {
|
|
1469
1659
|
const own = held.shown[index];
|
|
@@ -1472,7 +1662,8 @@ function compileInternal(source) {
|
|
|
1472
1662
|
}
|
|
1473
1663
|
held.geometry = geometry;
|
|
1474
1664
|
}
|
|
1475
|
-
|
|
1665
|
+
nodes.clear();
|
|
1666
|
+
slots.clear();
|
|
1476
1667
|
}
|
|
1477
1668
|
}
|
|
1478
1669
|
//# sourceMappingURL=graph.js.map
|