rig-c 2.31.0 → 2.31.1
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/package.json +1 -1
- package/src/meshreduce.ts +177 -47
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rig-c",
|
|
3
|
-
"version": "2.31.
|
|
3
|
+
"version": "2.31.1",
|
|
4
4
|
"description": "AI-authored Spine 2D rigging and animation, verified before it is written. rigc compiles a rig spec into Spine 4.3 skeleton data, gates it with named assertions held to a spine-core round trip's verdicts, and emits nothing that fails. Output imports into the Spine editor. Ships as an agent skill.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/meshreduce.ts
CHANGED
|
@@ -430,6 +430,98 @@ function edgeKey(a: number, b: number): string {
|
|
|
430
430
|
return a < b ? `${a},${b}` : `${b},${a}`;
|
|
431
431
|
}
|
|
432
432
|
|
|
433
|
+
/**
|
|
434
|
+
* Issue #1307: an undirected edge as one number, `min * n + max`, for ids below `n` — the key every edge set on the
|
|
435
|
+
* removal and boundary-run paths is held in, where `edgeKey` built a string per edge. Exact below 2^53, so for any
|
|
436
|
+
* `n` up to 94,906,265.
|
|
437
|
+
*/
|
|
438
|
+
function pairKey(a: number, b: number, n: number): number {
|
|
439
|
+
return a < b ? a * n + b : b * n + a;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Issue #1307: the edges `fresh`'s triangles hold that no triangle of `old` holds, each once, in the order `fresh`
|
|
444
|
+
* holds them (triangle by triangle, corner k to k + 1) — the order condition (b) reads them in and names the first
|
|
445
|
+
* over `weightJump`. Keyed by number over ids below `n`, and only the keys `fresh` holds are looked for in `old`.
|
|
446
|
+
* `addedEdgesByString` is the construction it replaced, kept as the one it is held equal to.
|
|
447
|
+
*/
|
|
448
|
+
function addedEdges(old: ReadonlyArray<readonly number[]>, fresh: ReadonlyArray<readonly number[]>, n: number): Array<[number, number]> {
|
|
449
|
+
const wanted = new Set<number>();
|
|
450
|
+
for (const t of fresh) for (let k = 0; k < 3; k++) wanted.add(pairKey(t[k], t[(k + 1) % 3], n));
|
|
451
|
+
const held = new Set<number>();
|
|
452
|
+
for (const t of old) {
|
|
453
|
+
for (let k = 0; k < 3; k++) {
|
|
454
|
+
const key = pairKey(t[k], t[(k + 1) % 3], n);
|
|
455
|
+
if (wanted.has(key)) held.add(key);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
const added: Array<[number, number]> = [];
|
|
459
|
+
const seen = new Set<number>();
|
|
460
|
+
for (const t of fresh) {
|
|
461
|
+
for (let k = 0; k < 3; k++) {
|
|
462
|
+
const key = pairKey(t[k], t[(k + 1) % 3], n);
|
|
463
|
+
if (held.has(key) || seen.has(key)) continue;
|
|
464
|
+
seen.add(key);
|
|
465
|
+
added.push([t[k], t[(k + 1) % 3]]);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return added;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/** The string-keyed construction `addedEdges` replaced (issue #1307), run only by the edge-set audit. */
|
|
472
|
+
function addedEdgesByString(old: ReadonlyArray<readonly number[]>, fresh: ReadonlyArray<readonly number[]>): Array<[number, number]> {
|
|
473
|
+
const had = new Set<string>();
|
|
474
|
+
for (const t of old) for (let k = 0; k < 3; k++) had.add(edgeKey(t[k], t[(k + 1) % 3]));
|
|
475
|
+
const added: Array<[number, number]> = [];
|
|
476
|
+
const seen = new Set<string>();
|
|
477
|
+
for (const t of fresh) {
|
|
478
|
+
for (let k = 0; k < 3; k++) {
|
|
479
|
+
const key = edgeKey(t[k], t[(k + 1) % 3]);
|
|
480
|
+
if (had.has(key) || seen.has(key)) continue;
|
|
481
|
+
seen.add(key);
|
|
482
|
+
added.push([t[k], t[(k + 1) % 3]]);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return added;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Issue #1307's audit, for the `mesh-compare` suite's control: every edge set the removal and boundary-run paths
|
|
490
|
+
* build is built a second time the string-keyed way it was before the issue and compared — the added edges as a list
|
|
491
|
+
* (membership and order, the order condition (b) reads), the triangulation's edge set the protected edges are looked
|
|
492
|
+
* up in as a set (its order is never read). A difference is recorded, not thrown, so the control reads every one.
|
|
493
|
+
* `plant` is the fault applied to the numeric construction before the comparison: one edge dropped from each list
|
|
494
|
+
* and each set, or each added list of two or more reversed. With the audit and no plant, the call's result is the
|
|
495
|
+
* call's without it.
|
|
496
|
+
*/
|
|
497
|
+
export interface EdgeSetAudit {
|
|
498
|
+
plant?: 'drop-one' | 'reorder';
|
|
499
|
+
/** Added-edge lists compared, and the edges in them. */
|
|
500
|
+
addedLists: number;
|
|
501
|
+
addedEdges: number;
|
|
502
|
+
/** Lists among them whose order a difference could move — two edges or more. */
|
|
503
|
+
orderedLists: number;
|
|
504
|
+
/** Triangulation edge sets compared (one per operation reaching the protected-edge check), and the edges in them. */
|
|
505
|
+
edgeSets: number;
|
|
506
|
+
edgeSetEdges: number;
|
|
507
|
+
/** Each difference found, named; the first few in full. */
|
|
508
|
+
differences: string[];
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/** One added-edge list read, through the audit when there is one: the plant applied, then the string construction compared. */
|
|
512
|
+
function auditedAdded(audit: EdgeSetAudit | null, where: string, added: Array<[number, number]>, byString: () => Array<[number, number]>): Array<[number, number]> {
|
|
513
|
+
if (audit === null) return added;
|
|
514
|
+
let read = added;
|
|
515
|
+
if (audit.plant === 'drop-one' && read.length > 0) read = read.slice(0, -1);
|
|
516
|
+
if (audit.plant === 'reorder' && read.length > 1) read = [...read].reverse();
|
|
517
|
+
const want = byString();
|
|
518
|
+
audit.addedLists++;
|
|
519
|
+
audit.addedEdges += want.length;
|
|
520
|
+
if (want.length > 1) audit.orderedLists++;
|
|
521
|
+
if (JSON.stringify(read) !== JSON.stringify(want)) audit.differences.push(`${where}: added edges ${JSON.stringify(read)} against the string construction's ${JSON.stringify(want)}`);
|
|
522
|
+
return read;
|
|
523
|
+
}
|
|
524
|
+
|
|
433
525
|
// ---------------------------------------------------------------------------
|
|
434
526
|
// the working mesh — vertices by stable id: source index, then insertion order
|
|
435
527
|
// ---------------------------------------------------------------------------
|
|
@@ -823,7 +915,10 @@ interface Run {
|
|
|
823
915
|
keyed: Map<number, { animation: string; ref: AttachmentRef; key: number; time: number }>;
|
|
824
916
|
protectedVertices: Set<number>;
|
|
825
917
|
protectedEdges: Array<[number, number]>;
|
|
826
|
-
|
|
918
|
+
/** The source's edges, `pairKey` over `work.nSource` (issue #1307). */
|
|
919
|
+
sourceEdges: Set<number>;
|
|
920
|
+
/** Issue #1307's audit of the edge sets, or null. */
|
|
921
|
+
edgeAudit: EdgeSetAudit | null;
|
|
827
922
|
/** The art's rasters, taken once for the call and read by every measurement in it (issue #1240). */
|
|
828
923
|
rasters: ArtRasters;
|
|
829
924
|
/** The step state each removal and insertion is measured through, carried from the last measurement (issue #1246); null measures every step in full. */
|
|
@@ -1356,8 +1451,17 @@ function refineRegions(run: Run): PhaseEnd {
|
|
|
1356
1451
|
// reduction (§1, §6)
|
|
1357
1452
|
// ---------------------------------------------------------------------------
|
|
1358
1453
|
|
|
1359
|
-
/**
|
|
1360
|
-
|
|
1454
|
+
/**
|
|
1455
|
+
* One removal tried: the working triangles after it and the edges it added — read through `added()`, built only when
|
|
1456
|
+
* read (issue #1307) — or the structural reason it cannot be made.
|
|
1457
|
+
*/
|
|
1458
|
+
type Removal = { triangles: Array<[number, number, number]>; added: () => Array<[number, number]> } | { blocked: string };
|
|
1459
|
+
|
|
1460
|
+
/**
|
|
1461
|
+
* One removal made on `work`'s triangles: the triangles after it — every triangle not touching `v` as it was, the
|
|
1462
|
+
* same tuple, then the hole's new ones, `fresh` — or the structural reason it cannot be made.
|
|
1463
|
+
*/
|
|
1464
|
+
function removalStep(work: Work, v: number): { triangles: Array<[number, number, number]>; fresh: Array<[number, number, number]> } | { blocked: string } {
|
|
1361
1465
|
const star = work.triangles.filter((t) => t.includes(v));
|
|
1362
1466
|
const next = new Map<number, number>();
|
|
1363
1467
|
const incoming = new Set<number>();
|
|
@@ -1398,19 +1502,16 @@ function removalOf(work: Work, v: number): { triangles: Array<[number, number, n
|
|
|
1398
1502
|
} else if (!boundary) {
|
|
1399
1503
|
return { blocked: `retriangulation: vertex ${v} has fewer than three neighbours` };
|
|
1400
1504
|
}
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
|
-
return { triangles: [...rest, ...fresh], added };
|
|
1505
|
+
return { triangles: [...rest, ...fresh], fresh };
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
/** `removalStep` with the edges it adds: those of the hole's new triangles no triangle before it holds, in their order. */
|
|
1509
|
+
function removalOf(work: Work, v: number, audit: EdgeSetAudit | null = null): Removal {
|
|
1510
|
+
const step = removalStep(work, v);
|
|
1511
|
+
if ('blocked' in step) return step;
|
|
1512
|
+
const old = work.triangles;
|
|
1513
|
+
const n = work.alive.length;
|
|
1514
|
+
return { triangles: step.triangles, added: () => auditedAdded(audit, `removing ${v}`, addedEdges(old, step.fresh, n), () => addedEdgesByString(old, step.fresh)) };
|
|
1414
1515
|
}
|
|
1415
1516
|
|
|
1416
1517
|
/** L1 difference of two weight vectors over the bones they name. */
|
|
@@ -1447,10 +1548,10 @@ function loadOrder(run: Run): { order: number[]; load: Map<number, number> } {
|
|
|
1447
1548
|
const order: number[] = [];
|
|
1448
1549
|
for (let v = 0; v < work.nSource; v++) {
|
|
1449
1550
|
if (!work.alive[v] || run.protectedVertices.has(v)) continue;
|
|
1450
|
-
const step = removalOf(work, v);
|
|
1551
|
+
const step = removalOf(work, v, run.edgeAudit);
|
|
1451
1552
|
let most = 0;
|
|
1452
1553
|
if ('blocked' in step) most = Infinity;
|
|
1453
|
-
else for (const [a, b] of step.added) most = Math.max(most, edgeLoad(work, a, b));
|
|
1554
|
+
else for (const [a, b] of step.added()) most = Math.max(most, edgeLoad(work, a, b));
|
|
1454
1555
|
load.set(v, most);
|
|
1455
1556
|
order.push(v);
|
|
1456
1557
|
}
|
|
@@ -1470,32 +1571,27 @@ function loadOrder(run: Run): { order: number[]; load: Map<number, number> } {
|
|
|
1470
1571
|
* structural reason one of the removals cannot be made. The outline loses the run and gains the chord from the
|
|
1471
1572
|
* vertex before it to the vertex after it; nothing is measured in between.
|
|
1472
1573
|
*/
|
|
1473
|
-
function runRemovalOf(work: Work, vertices: readonly number[]
|
|
1574
|
+
function runRemovalOf(work: Work, vertices: readonly number[], audit: EdgeSetAudit | null = null): Removal {
|
|
1474
1575
|
const was = work.triangles;
|
|
1576
|
+
// Issue #1307: each removal's own added edges are never read here, so none is built; what the run adds is read off
|
|
1577
|
+
// the triangles its removals made. A triangle of `after` that no removal made is the same tuple as one of `was`
|
|
1578
|
+
// (`removalStep` keeps every untouched triangle as it was), so every edge it holds is old and it adds nothing.
|
|
1579
|
+
const made = new Set<readonly number[]>();
|
|
1475
1580
|
let after: Array<[number, number, number]>;
|
|
1476
1581
|
try {
|
|
1477
1582
|
for (const v of vertices) {
|
|
1478
|
-
const step =
|
|
1583
|
+
const step = removalStep(work, v);
|
|
1479
1584
|
if ('blocked' in step) return step;
|
|
1585
|
+
for (const t of step.fresh) made.add(t);
|
|
1480
1586
|
work.triangles = step.triangles;
|
|
1481
1587
|
}
|
|
1482
1588
|
after = work.triangles;
|
|
1483
1589
|
} finally {
|
|
1484
1590
|
work.triangles = was;
|
|
1485
1591
|
}
|
|
1486
|
-
const
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
const seen = new Set<string>();
|
|
1490
|
-
for (const t of after) {
|
|
1491
|
-
for (let k = 0; k < 3; k++) {
|
|
1492
|
-
const key = edgeKey(t[k], t[(k + 1) % 3]);
|
|
1493
|
-
if (old.has(key) || seen.has(key)) continue;
|
|
1494
|
-
seen.add(key);
|
|
1495
|
-
added.push([t[k], t[(k + 1) % 3]]);
|
|
1496
|
-
}
|
|
1497
|
-
}
|
|
1498
|
-
return { triangles: after, added };
|
|
1592
|
+
const n = work.alive.length;
|
|
1593
|
+
const label = `removing ${vertices.join(', ')} as one boundary run`;
|
|
1594
|
+
return { triangles: after, added: () => auditedAdded(audit, label, addedEdges(was, after.filter((t) => made.has(t)), n), () => addedEdgesByString(was, after)) };
|
|
1499
1595
|
}
|
|
1500
1596
|
|
|
1501
1597
|
/**
|
|
@@ -1629,6 +1725,28 @@ function deviationFloor(run: Run, mesh: SourceMesh, vertices: readonly number[])
|
|
|
1629
1725
|
*/
|
|
1630
1726
|
const FLOOR_MARGIN = 1e-5;
|
|
1631
1727
|
|
|
1728
|
+
/**
|
|
1729
|
+
* Issue #1307's audit of the triangulation's edge set (`EdgeSetAudit`): the plant applied to `edges` in place — so
|
|
1730
|
+
* the decision reads the planted set — then the set held to the string-keyed one, member for member.
|
|
1731
|
+
*/
|
|
1732
|
+
function auditEdgeSet(audit: EdgeSetAudit, edges: Set<number>, triangles: ReadonlyArray<readonly number[]>, n: number, vertices: readonly number[]): void {
|
|
1733
|
+
if (audit.plant === 'drop-one') {
|
|
1734
|
+
const first = edges.values().next();
|
|
1735
|
+
if (first.done !== true) edges.delete(first.value);
|
|
1736
|
+
}
|
|
1737
|
+
const want = new Set<string>();
|
|
1738
|
+
for (const t of triangles) for (let k = 0; k < 3; k++) want.add(edgeKey(t[k], t[(k + 1) % 3]));
|
|
1739
|
+
audit.edgeSets++;
|
|
1740
|
+
audit.edgeSetEdges += want.size;
|
|
1741
|
+
const missing = [...want].filter((key) => {
|
|
1742
|
+
const [a, b] = key.split(',').map(Number);
|
|
1743
|
+
return !edges.has(pairKey(a, b, n));
|
|
1744
|
+
});
|
|
1745
|
+
if (missing.length > 0 || edges.size !== want.size) {
|
|
1746
|
+
audit.differences.push(`removing ${vertices.join(', ')}: the triangulation's edge set holds ${edges.size} edge(s) against the string construction's ${want.size}${missing.length > 0 ? `, missing ${missing.slice(0, 3).join(' ')}` : ''}`);
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1632
1750
|
/**
|
|
1633
1751
|
* One operation — a single removal, or a boundary run of two or more — tried: null when it was taken (the working
|
|
1634
1752
|
* mesh then holds it), else what refused it (the working mesh as it was). `full` measures every candidate that
|
|
@@ -1637,12 +1755,12 @@ const FLOOR_MARGIN = 1e-5;
|
|
|
1637
1755
|
*/
|
|
1638
1756
|
function tryOperation(run: Run, vertices: readonly number[], full: boolean): Refusal | null {
|
|
1639
1757
|
const { work, input } = run;
|
|
1640
|
-
const step = vertices.length === 1 ? removalOf(work, vertices[0]) : runRemovalOf(work, vertices);
|
|
1758
|
+
const step = vertices.length === 1 ? removalOf(work, vertices[0], run.edgeAudit) : runRemovalOf(work, vertices, run.edgeAudit);
|
|
1641
1759
|
if ('blocked' in step) return step.blocked;
|
|
1642
1760
|
const jump = input.protect.weightJump;
|
|
1643
1761
|
if (jump !== null) {
|
|
1644
|
-
for (const [a, b] of step.added) {
|
|
1645
|
-
if (a < work.nSource && b < work.nSource && run.sourceEdges.has(
|
|
1762
|
+
for (const [a, b] of step.added()) {
|
|
1763
|
+
if (a < work.nSource && b < work.nSource && run.sourceEdges.has(pairKey(a, b, work.nSource))) continue;
|
|
1646
1764
|
const d = weightJump(work, a, b);
|
|
1647
1765
|
if (d > jump) return `weightJump (b): the new edge ${a}–${b} joins weight vectors ${r6(d)} apart, over ${jump}`;
|
|
1648
1766
|
}
|
|
@@ -1654,12 +1772,17 @@ function tryOperation(run: Run, vertices: readonly number[], full: boolean): Ref
|
|
|
1654
1772
|
work.triangles = was.triangles;
|
|
1655
1773
|
for (const v of vertices) work.alive[v] = true;
|
|
1656
1774
|
};
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1775
|
+
// Issue #1307: the triangulation's edge set is built only when a protected edge is looked up in it, keyed by number.
|
|
1776
|
+
if (run.protectedEdges.length > 0) {
|
|
1777
|
+
const n = work.alive.length;
|
|
1778
|
+
const edges = new Set<number>();
|
|
1779
|
+
for (const t of work.triangles) for (let k = 0; k < 3; k++) edges.add(pairKey(t[k], t[(k + 1) % 3], n));
|
|
1780
|
+
if (run.edgeAudit !== null) auditEdgeSet(run.edgeAudit, edges, work.triangles, n, vertices);
|
|
1781
|
+
for (const [a, b] of run.protectedEdges) {
|
|
1782
|
+
if (!edges.has(pairKey(a, b, n))) {
|
|
1783
|
+
undo();
|
|
1784
|
+
return `protect (a): the protected source edge ${a}–${b} is no longer an edge`;
|
|
1785
|
+
}
|
|
1663
1786
|
}
|
|
1664
1787
|
}
|
|
1665
1788
|
let canon: Canonical;
|
|
@@ -1841,7 +1964,7 @@ function delaunayFlips(run: Run, plant: ReductionPlant | null): FlipResult {
|
|
|
1841
1964
|
const sine = oppositeAngleSine(P[a], P[b], P[c], P[d]);
|
|
1842
1965
|
if (against ? !(sine > FLIP_MARGIN) : !(sine < -FLIP_MARGIN)) continue;
|
|
1843
1966
|
if (held(a, b) || held(c, d)) continue;
|
|
1844
|
-
if (jump !== null && !(c < work.nSource && d < work.nSource && run.sourceEdges.has(
|
|
1967
|
+
if (jump !== null && !(c < work.nSource && d < work.nSource && run.sourceEdges.has(pairKey(c, d, work.nSource))) && weightJump(work, c, d) > jump) continue;
|
|
1845
1968
|
if (flips >= maxFlips) return { bound: `the flip bound n(n - 1)/2 = ${maxFlips} over ${alive} vertices was reached`, flips, sweeps };
|
|
1846
1969
|
tris[i] = [a, d, c];
|
|
1847
1970
|
tris[j] = [d, b, c];
|
|
@@ -1961,12 +2084,13 @@ export function reduceMeshWith(
|
|
|
1961
2084
|
plant: ReductionPlant | null = null,
|
|
1962
2085
|
observe: AttemptObserver | null = null,
|
|
1963
2086
|
skinning: SkinningHooks | null = null,
|
|
2087
|
+
edgeAudit: EdgeSetAudit | null = null,
|
|
1964
2088
|
): MeshReductionResult {
|
|
1965
2089
|
validateReduction(input, plant);
|
|
1966
2090
|
if (steps !== null && steps.rasters !== rasters) {
|
|
1967
2091
|
refuse('REDUCE_ART_RASTERS_MISMATCH', `attachment ${nameOf(input.attachment)}: the step rasters were made over another rasters object; required step rasters made over the rasters passed beside them (stepRastersOf(rasters))`);
|
|
1968
2092
|
}
|
|
1969
|
-
return reduceValidated(input, rasters, steps, plant, observe, skinning);
|
|
2093
|
+
return reduceValidated(input, rasters, steps, plant, observe, skinning, edgeAudit);
|
|
1970
2094
|
}
|
|
1971
2095
|
|
|
1972
2096
|
/**
|
|
@@ -1980,6 +2104,7 @@ function reduceValidated(
|
|
|
1980
2104
|
plant: ReductionPlant | null = null,
|
|
1981
2105
|
observe: AttemptObserver | null = null,
|
|
1982
2106
|
hooks: SkinningHooks | null = null,
|
|
2107
|
+
edgeAudit: EdgeSetAudit | null = null,
|
|
1983
2108
|
): MeshReductionResult {
|
|
1984
2109
|
const who = `attachment ${nameOf(input.attachment)}`;
|
|
1985
2110
|
const src = input.source;
|
|
@@ -2047,8 +2172,12 @@ function reduceValidated(
|
|
|
2047
2172
|
sourceTriangle: new Map(),
|
|
2048
2173
|
};
|
|
2049
2174
|
for (let i = 0; i + 2 < src.triangles.length; i += 3) work.triangles.push([src.triangles[i], src.triangles[i + 1], src.triangles[i + 2]]);
|
|
2050
|
-
|
|
2051
|
-
|
|
2175
|
+
// The string-keyed set is read once, by `protectionOf`, whose weightJump edges follow its sorted keys; every step
|
|
2176
|
+
// reads the numeric one (issue #1307).
|
|
2177
|
+
const sourceEdgeNames = new Set<string>();
|
|
2178
|
+
for (const t of work.triangles) for (let k = 0; k < 3; k++) sourceEdgeNames.add(edgeKey(t[k], t[(k + 1) % 3]));
|
|
2179
|
+
const sourceEdges = new Set<number>();
|
|
2180
|
+
for (const t of work.triangles) for (let k = 0; k < 3; k++) sourceEdges.add(pairKey(t[k], t[(k + 1) % 3], work.nSource));
|
|
2052
2181
|
const run: Run = {
|
|
2053
2182
|
input,
|
|
2054
2183
|
who,
|
|
@@ -2062,8 +2191,9 @@ function reduceValidated(
|
|
|
2062
2191
|
observe,
|
|
2063
2192
|
tally: { sharesDroppedOnGrid: 0, sharesPruned: 0 },
|
|
2064
2193
|
keyed: keyedSourceVertices(input),
|
|
2065
|
-
...protectionOf(input, work,
|
|
2194
|
+
...protectionOf(input, work, sourceEdgeNames),
|
|
2066
2195
|
sourceEdges,
|
|
2196
|
+
edgeAudit,
|
|
2067
2197
|
rasters,
|
|
2068
2198
|
stepRasters: steps,
|
|
2069
2199
|
skinning: null,
|