@jbrowse/bandage-core 7.0.0 → 7.2.0
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/chunks/{chunk-EZPBJPK6.js → chunk-3JANT2SW.js} +6 -3
- package/dist/chunks/chunk-6EJIU6IA.js +42 -0
- package/dist/chunks/{chunk-6UFS3EWI.js → chunk-EPWYRSHH.js} +1 -1
- package/dist/chunks/{chunk-WEMKWLDF.js → chunk-JKXE6CIP.js} +1 -1
- package/dist/chunks/{chunk-YZ5CCEDH.js → chunk-JVLURPFW.js} +1 -0
- package/dist/chunks/{chunk-MZ7QQRUJ.js → chunk-KREASK3Z.js} +7 -13
- package/dist/chunks/chunk-LYJ4US66.js +664 -0
- package/dist/chunks/{chunk-7PWMTUAK.js → chunk-QMJ4J5PT.js} +14 -13
- package/dist/chunks/{chunk-EOSVRILX.js → chunk-S73EYTZA.js} +1 -1
- package/dist/chunks/{chunk-JFNPL4UT.js → chunk-UGD6ZMOF.js} +29 -4
- package/dist/chunks/{chunk-O4TGF4WS.js → chunk-UNOE2NDN.js} +155 -129
- package/dist/chunks/{chunk-AWCMVPVP.js → chunk-Y7UR3MDE.js} +1 -1
- package/dist/cli/figure.js +24 -13
- package/dist/figure.js +9 -9
- package/dist/index.js +28 -19
- package/dist/layout/deletionRoutes.js +3 -1
- package/dist/layout/stressEngine.js +5 -5
- package/dist/layout/stressPlacement.js +7 -0
- package/dist/layout/walkRowDraw.js +11 -1
- package/dist/layout/walkRowLayout.js +1 -1
- package/dist/layout/walkStrip.js +4 -3
- package/dist/layoutEngines.js +10 -2
- package/dist/layoutModes.js +2 -2
- package/dist/pipeline.js +2 -2
- package/dist/types/cli/figure.d.ts +1 -1
- package/dist/types/figure.d.ts +2 -1
- package/dist/types/index.d.ts +5 -5
- package/dist/types/layout/deletionRoutes.d.ts +1 -0
- package/dist/types/layout/stressEngine.d.ts +19 -11
- package/dist/types/layout/stressPlacement.d.ts +5 -0
- package/dist/types/layout/walkRowDraw.d.ts +11 -0
- package/dist/types/layout/walkRowLayout.d.ts +2 -1
- package/dist/types/layout/walkStrip.d.ts +1 -1
- package/dist/types/layoutEngines.d.ts +12 -0
- package/dist/types/pipeline.d.ts +1 -1
- package/dist/types/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/viewport.js +3 -3
- package/package.json +1 -1
- package/dist/chunks/chunk-L53SXJZI.js +0 -22
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Canvas2DRenderer
|
|
3
|
+
} from "./chunk-CH3DLBUT.js";
|
|
1
4
|
import {
|
|
2
5
|
svgCanvas
|
|
3
6
|
} from "./chunk-47DYCZOK.js";
|
|
@@ -5,15 +8,12 @@ import {
|
|
|
5
8
|
stripGeneGaps,
|
|
6
9
|
walkStripFrame,
|
|
7
10
|
walkStripLabelsTree
|
|
8
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-KREASK3Z.js";
|
|
9
12
|
import {
|
|
10
13
|
walkRowsKey,
|
|
11
14
|
walkRowsKeyTree,
|
|
12
15
|
walkRowsTree
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import {
|
|
15
|
-
Canvas2DRenderer
|
|
16
|
-
} from "./chunk-CH3DLBUT.js";
|
|
16
|
+
} from "./chunk-UGD6ZMOF.js";
|
|
17
17
|
import {
|
|
18
18
|
EXON_COLOR,
|
|
19
19
|
exonOutlineTree,
|
|
@@ -24,10 +24,10 @@ import {
|
|
|
24
24
|
} from "./chunk-BUBZ32IC.js";
|
|
25
25
|
import {
|
|
26
26
|
version
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-S73EYTZA.js";
|
|
28
28
|
import {
|
|
29
29
|
axisScaleOf
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-Y7UR3MDE.js";
|
|
31
31
|
import {
|
|
32
32
|
facetLifts,
|
|
33
33
|
walkLift
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
FIT_PADDING,
|
|
44
44
|
drawingBounds,
|
|
45
45
|
fitTransform
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-EPWYRSHH.js";
|
|
47
47
|
import {
|
|
48
48
|
buildGeometry,
|
|
49
49
|
computeReferenceRamp
|
|
@@ -59,7 +59,7 @@ import {
|
|
|
59
59
|
} from "./chunk-FP4H7SFT.js";
|
|
60
60
|
import {
|
|
61
61
|
walkRowsExtent
|
|
62
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-3JANT2SW.js";
|
|
63
63
|
import {
|
|
64
64
|
pathLegend
|
|
65
65
|
} from "./chunk-E7FWQQHP.js";
|
|
@@ -154,7 +154,7 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
154
154
|
const rows = o.walkRows;
|
|
155
155
|
const bounds = drawingBounds(layout, {
|
|
156
156
|
region: o.fitToDrawing ? void 0 : region,
|
|
157
|
-
extent: rows && layout.extent ? walkRowsExtent(rows) : void 0
|
|
157
|
+
extent: rows && layout.extent ? walkRowsExtent(rows, o.walkRowPitch) : void 0
|
|
158
158
|
});
|
|
159
159
|
const pixelRows = layout.pixelRows ?? false;
|
|
160
160
|
const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
|
|
@@ -226,7 +226,7 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
226
226
|
serializeEl(
|
|
227
227
|
walkRowsTree(
|
|
228
228
|
rows,
|
|
229
|
-
{ ...t, width: w, height: h },
|
|
229
|
+
{ ...t, width: w, height: h, ...o.walkRowPitch },
|
|
230
230
|
{
|
|
231
231
|
ramp: rampInterval,
|
|
232
232
|
rowGenes: o.rowGenes
|
|
@@ -235,9 +235,10 @@ function figureSvg(graph, layout, o = {}) {
|
|
|
235
235
|
)
|
|
236
236
|
);
|
|
237
237
|
}
|
|
238
|
-
const
|
|
238
|
+
const pitch = o.walkRowPitch;
|
|
239
|
+
const rowLabels = rows ? pitch && !pitch.labelled ? [] : [rows.reference, ...rows.rows].map((r, i) => ({
|
|
239
240
|
label: r.label,
|
|
240
|
-
y: i * ROW_HEIGHT_PX
|
|
241
|
+
y: i * (pitch?.rowPx ?? ROW_HEIGHT_PX)
|
|
241
242
|
})) : layout.rowLabels ?? [];
|
|
242
243
|
for (const { label, y } of rowLabels) {
|
|
243
244
|
const sy = y * t.scaleY + t.translateY;
|
|
@@ -24,6 +24,9 @@ var OUTSIDE_CUT = "#bdbdbd";
|
|
|
24
24
|
var GENE_INK = "#1c1c22";
|
|
25
25
|
var BAR_PX = 12;
|
|
26
26
|
var GAP_PX = 4;
|
|
27
|
+
var LABELLED_ROW_PX = 12;
|
|
28
|
+
var READOUT_ROW_PX = 14;
|
|
29
|
+
var MIN_TILED_BAR_PX = 6;
|
|
27
30
|
var MIN_TILE_PX = 3;
|
|
28
31
|
function kb(bp) {
|
|
29
32
|
return `${(bp / 1e3).toFixed(bp < 1e4 ? 1 : 0)} kb`;
|
|
@@ -209,6 +212,9 @@ function walkRowsKey(bars, o = {}) {
|
|
|
209
212
|
var CALL_TICK = "#111";
|
|
210
213
|
var UNBACKED_TICK = "#9e9e9e";
|
|
211
214
|
var DISAGREES = "#c62828";
|
|
215
|
+
function callTickInk(call) {
|
|
216
|
+
return call.agrees === false ? DISAGREES : call.spanningReads === 0 ? UNBACKED_TICK : CALL_TICK;
|
|
217
|
+
}
|
|
212
218
|
function walkRowsKeyTree(entries, x, y, idPrefix = "walk-key") {
|
|
213
219
|
let at = 0;
|
|
214
220
|
const parts = entries.flatMap((e, i) => {
|
|
@@ -262,6 +268,19 @@ function walkRowsKeyTree(entries, x, y, idPrefix = "walk-key") {
|
|
|
262
268
|
)
|
|
263
269
|
};
|
|
264
270
|
}
|
|
271
|
+
function rowPitch(count, room, minRowPx = 0) {
|
|
272
|
+
const fit = room / Math.max(1, count);
|
|
273
|
+
const rowPx = Math.max(
|
|
274
|
+
minRowPx,
|
|
275
|
+
Math.min(ROW_HEIGHT_PX, fit >= 1 ? Math.floor(fit) : fit)
|
|
276
|
+
);
|
|
277
|
+
return {
|
|
278
|
+
rowPx,
|
|
279
|
+
barPx: rowPx <= 3 ? rowPx : Math.max(3, Math.round(rowPx * 0.6)),
|
|
280
|
+
labelled: rowPx >= LABELLED_ROW_PX,
|
|
281
|
+
readouts: rowPx >= READOUT_ROW_PX
|
|
282
|
+
};
|
|
283
|
+
}
|
|
265
284
|
function coalesceRuns(runs, pxPerBp) {
|
|
266
285
|
const minBp = 1 / pxPerBp;
|
|
267
286
|
const out = [];
|
|
@@ -358,6 +377,7 @@ function walkRowsTree(bars, frame, o = {}) {
|
|
|
358
377
|
const X = (bp) => bp * frame.scaleX + frame.translateX;
|
|
359
378
|
const rowPx = frame.rowPx ?? ROW_HEIGHT_PX;
|
|
360
379
|
const barPx = frame.barPx ?? BAR_PX;
|
|
380
|
+
const overhang = Math.min(3, Math.max(0, (rowPx - barPx) / 2));
|
|
361
381
|
const Y = (row) => row * rowPx * frame.scaleY + frame.translateY;
|
|
362
382
|
const { origin, unit, reference, rows } = bars;
|
|
363
383
|
const along = (offset) => X(origin + offset);
|
|
@@ -392,7 +412,7 @@ function walkRowsTree(bars, frame, o = {}) {
|
|
|
392
412
|
rect
|
|
393
413
|
] : [rect];
|
|
394
414
|
});
|
|
395
|
-
const ticks = unit ? unitTicks(row.bp, unit, frame.scaleX).map(
|
|
415
|
+
const ticks = unit && barPx >= MIN_TILED_BAR_PX ? unitTicks(row.bp, unit, frame.scaleX).map(
|
|
396
416
|
(k) => el("line", {
|
|
397
417
|
x1: along(k),
|
|
398
418
|
x2: along(k),
|
|
@@ -425,10 +445,10 @@ function walkRowsTree(bars, frame, o = {}) {
|
|
|
425
445
|
call && el("rect", {
|
|
426
446
|
"data-testid": "graph-walk-call",
|
|
427
447
|
x: along(call.bp) - 1,
|
|
428
|
-
y: y - barPx / 2 -
|
|
448
|
+
y: y - barPx / 2 - overhang,
|
|
429
449
|
width: 2,
|
|
430
|
-
height: barPx +
|
|
431
|
-
fill: call
|
|
450
|
+
height: barPx + 2 * overhang,
|
|
451
|
+
fill: callTickInk(call)
|
|
432
452
|
}),
|
|
433
453
|
readouts && el(
|
|
434
454
|
"text",
|
|
@@ -457,6 +477,7 @@ export {
|
|
|
457
477
|
GENE_INK,
|
|
458
478
|
BAR_PX,
|
|
459
479
|
GAP_PX,
|
|
480
|
+
LABELLED_ROW_PX,
|
|
460
481
|
kb,
|
|
461
482
|
walkRowReadout,
|
|
462
483
|
readoutPlacement,
|
|
@@ -469,8 +490,12 @@ export {
|
|
|
469
490
|
boxesGenes,
|
|
470
491
|
rowGeneBoxes,
|
|
471
492
|
walkRowsKey,
|
|
493
|
+
CALL_TICK,
|
|
494
|
+
UNBACKED_TICK,
|
|
472
495
|
DISAGREES,
|
|
496
|
+
callTickInk,
|
|
473
497
|
walkRowsKeyTree,
|
|
498
|
+
rowPitch,
|
|
474
499
|
coalesceRuns,
|
|
475
500
|
walkRowsTree
|
|
476
501
|
};
|
|
@@ -1,21 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
stressPlacement
|
|
3
|
+
} from "./chunk-LYJ4US66.js";
|
|
1
4
|
import {
|
|
2
5
|
isBackbone
|
|
3
6
|
} from "./chunk-FYQWPEEY.js";
|
|
4
7
|
|
|
5
8
|
// src/layout/stressEngine.ts
|
|
6
|
-
var LAYOUT_ENGINES = [
|
|
7
|
-
{
|
|
8
|
-
value: "fmmm",
|
|
9
|
-
label: "Bandage (FMMM)",
|
|
10
|
-
description: "Bandage's force-directed layout: OGDF's fast multipole multilevel method, compiled to wasm."
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
value: "stress",
|
|
14
|
-
label: "Stress (experimental)",
|
|
15
|
-
description: "Stress layout by stochastic gradient descent in plain JS: distances along the graph become distances on the page, so the reference reads straight and a repeat loop reads as a ring."
|
|
16
|
-
}
|
|
17
|
-
];
|
|
18
|
-
var LAYOUT_ENGINE_VALUES = LAYOUT_ENGINES.map((e) => e.value);
|
|
19
9
|
var QUALITY = [
|
|
20
10
|
{ walks: 2, iterations: 15 },
|
|
21
11
|
{ walks: 4, iterations: 30 },
|
|
@@ -24,11 +14,12 @@ var QUALITY = [
|
|
|
24
14
|
{ walks: 16, iterations: 120 }
|
|
25
15
|
];
|
|
26
16
|
var UNTANGLE_PASSES = 20;
|
|
27
|
-
var
|
|
17
|
+
var UNTANGLE_LINKS = 3;
|
|
28
18
|
var UNTANGLE_PUSH = 0.3;
|
|
29
19
|
var SMOOTH_PASSES = 2;
|
|
30
20
|
var MAX_HOPS = 200;
|
|
31
21
|
var EPS = 0.1;
|
|
22
|
+
var WARM = 0.5;
|
|
32
23
|
function flipped(strand, name) {
|
|
33
24
|
const own = name.at(-1);
|
|
34
25
|
return (strand === "+" || strand === "-") && (own === "+" || own === "-") && strand !== own;
|
|
@@ -137,12 +128,36 @@ function chains(request) {
|
|
|
137
128
|
off,
|
|
138
129
|
nb,
|
|
139
130
|
wt,
|
|
140
|
-
n
|
|
131
|
+
n,
|
|
132
|
+
link
|
|
141
133
|
};
|
|
142
134
|
}
|
|
135
|
+
function components(c) {
|
|
136
|
+
const { n, off, nb } = c;
|
|
137
|
+
const comp = new Int32Array(n).fill(-1);
|
|
138
|
+
const members = [];
|
|
139
|
+
for (let s = 0; s < n; s++) {
|
|
140
|
+
if (comp[s] >= 0) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const queue = [s];
|
|
144
|
+
comp[s] = members.length;
|
|
145
|
+
for (const u of queue) {
|
|
146
|
+
for (let e = off[u]; e < off[u + 1]; e++) {
|
|
147
|
+
const v = nb[e];
|
|
148
|
+
if (comp[v] < 0) {
|
|
149
|
+
comp[v] = members.length;
|
|
150
|
+
queue.push(v);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
members.push(queue);
|
|
155
|
+
}
|
|
156
|
+
return { comp, members };
|
|
157
|
+
}
|
|
143
158
|
function stressLayout(request, opts = {}) {
|
|
144
159
|
const c = chains(request);
|
|
145
|
-
const { n, off, nb, wt, seedX, seedY, backbone, first, count, per } = c;
|
|
160
|
+
const { n, off, nb, wt, seedX, seedY, backbone, first, count, per, link } = c;
|
|
146
161
|
const o = request.options;
|
|
147
162
|
const quality = QUALITY[Math.max(0, Math.min(4, num(opts.quality ?? o.quality, 2)))];
|
|
148
163
|
const gap = num(opts.componentSeparation ?? o.componentSeparation, 15);
|
|
@@ -153,6 +168,7 @@ function stressLayout(request, opts = {}) {
|
|
|
153
168
|
if (n === 0) {
|
|
154
169
|
return { nodePositions };
|
|
155
170
|
}
|
|
171
|
+
const { comp, members } = components(c);
|
|
156
172
|
let dmin = Infinity;
|
|
157
173
|
let total = 0;
|
|
158
174
|
for (let e = 0; e < nb.length; e++) {
|
|
@@ -167,9 +183,13 @@ function stressLayout(request, opts = {}) {
|
|
|
167
183
|
break;
|
|
168
184
|
}
|
|
169
185
|
}
|
|
186
|
+
const placed = anySeed ? stressPlacement(c) : void 0;
|
|
170
187
|
const side = Math.sqrt(n) * 20;
|
|
171
188
|
for (let i = 0; i < n; i++) {
|
|
172
|
-
if (
|
|
189
|
+
if (placed && !Number.isNaN(placed.X[i])) {
|
|
190
|
+
X[i] = placed.X[i];
|
|
191
|
+
Y[i] = placed.Y[i];
|
|
192
|
+
} else if (anySeed && !Number.isNaN(seedX[i])) {
|
|
173
193
|
X[i] = seedX[i] + rand() - 0.5;
|
|
174
194
|
Y[i] = seedY[i] + rand() - 0.5;
|
|
175
195
|
} else {
|
|
@@ -177,15 +197,18 @@ function stressLayout(request, opts = {}) {
|
|
|
177
197
|
Y[i] = rand() * side;
|
|
178
198
|
}
|
|
179
199
|
}
|
|
180
|
-
const refIdx =
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
200
|
+
const refIdx = members.map(
|
|
201
|
+
(points) => Int32Array.from(
|
|
202
|
+
points.filter((i) => backbone[i] && !Number.isNaN(seedX[i])).sort((a, b) => seedX[a] - seedX[b])
|
|
203
|
+
)
|
|
204
|
+
);
|
|
205
|
+
const refX = refIdx.map((idx) => Float64Array.from(idx, (i) => seedX[i]));
|
|
206
|
+
let span = 0;
|
|
207
|
+
for (const xs of refX) {
|
|
208
|
+
if (xs.length) {
|
|
209
|
+
span = Math.max(span, xs[xs.length - 1] - xs[0]);
|
|
184
210
|
}
|
|
185
211
|
}
|
|
186
|
-
refIdx.sort((a, b) => seedX[a] - seedX[b]);
|
|
187
|
-
const refX = Float64Array.from(refIdx, (i) => seedX[i]);
|
|
188
|
-
const span = refIdx.length ? refX[refX.length - 1] - refX[0] : 0;
|
|
189
212
|
const dmax = Math.max(span, total / 4, dmin * 10);
|
|
190
213
|
const etaMax = dmax * dmax;
|
|
191
214
|
const etaMin = EPS * dmin * dmin;
|
|
@@ -209,6 +232,7 @@ function stressLayout(request, opts = {}) {
|
|
|
209
232
|
X[j] = X[j] + rx;
|
|
210
233
|
Y[j] = Y[j] + ry;
|
|
211
234
|
};
|
|
235
|
+
let walked = 0;
|
|
212
236
|
const walk = (i, length) => {
|
|
213
237
|
let chain = c.chainOf[i];
|
|
214
238
|
let idx = c.indexOf[i];
|
|
@@ -219,48 +243,57 @@ function stressLayout(request, opts = {}) {
|
|
|
219
243
|
const step = per[chain];
|
|
220
244
|
const toEnd = (dir > 0 ? points - 1 - idx : idx) * step;
|
|
221
245
|
if (remaining <= toEnd) {
|
|
222
|
-
|
|
246
|
+
const steps = Math.round(remaining / step);
|
|
247
|
+
walked = length - remaining + steps * step;
|
|
248
|
+
return first[chain] + idx + dir * steps;
|
|
223
249
|
}
|
|
224
250
|
remaining -= toEnd;
|
|
225
251
|
const out = c.links[chain][dir > 0 ? 1 : 0];
|
|
226
252
|
if (out.length === 0) {
|
|
253
|
+
walked = length - remaining;
|
|
227
254
|
return first[chain] + (dir > 0 ? points - 1 : 0);
|
|
228
255
|
}
|
|
229
256
|
const [next, enterSide] = out[Math.floor(rand() * out.length)];
|
|
230
|
-
remaining -=
|
|
257
|
+
remaining -= link;
|
|
231
258
|
chain = next;
|
|
232
259
|
dir = enterSide === 0 ? 1 : -1;
|
|
233
260
|
idx = enterSide === 0 ? 0 : count[next] - 1;
|
|
234
261
|
if (remaining <= 0) {
|
|
262
|
+
walked = length - remaining;
|
|
235
263
|
return first[chain] + idx;
|
|
236
264
|
}
|
|
237
265
|
}
|
|
238
266
|
return -1;
|
|
239
267
|
};
|
|
240
|
-
const
|
|
241
|
-
const
|
|
242
|
-
|
|
268
|
+
const referenceTerm = (i, eta) => {
|
|
269
|
+
const idx = refIdx[comp[i]];
|
|
270
|
+
if (!backbone[i] || idx.length < 2 || Number.isNaN(seedX[i])) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const xs = refX[comp[i]];
|
|
274
|
+
const target = seedX[i] + Math.exp(logMin + rand() * (logMax - logMin)) * (rand() < 0.5 ? -1 : 1);
|
|
243
275
|
let lo = 0;
|
|
244
|
-
let hi =
|
|
276
|
+
let hi = xs.length;
|
|
245
277
|
while (lo < hi) {
|
|
246
278
|
const mid = lo + hi >> 1;
|
|
247
|
-
if (
|
|
279
|
+
if (xs[mid] < target) {
|
|
248
280
|
lo = mid + 1;
|
|
249
281
|
} else {
|
|
250
282
|
hi = mid;
|
|
251
283
|
}
|
|
252
284
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if (!backbone[i] || refIdx.length < 2 || Number.isNaN(seedX[i])) {
|
|
285
|
+
const j = idx[Math.min(lo, idx.length - 1)];
|
|
286
|
+
const d = seedX[j] - seedX[i];
|
|
287
|
+
if (j === i || d === 0) {
|
|
257
288
|
return;
|
|
258
289
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
move(i, j, d, eta);
|
|
290
|
+
let mu = eta / (d * d);
|
|
291
|
+
if (mu > 1) {
|
|
292
|
+
mu = 1;
|
|
263
293
|
}
|
|
294
|
+
const r = mu * (X[j] - X[i] - d) / 2;
|
|
295
|
+
X[i] = X[i] + r;
|
|
296
|
+
X[j] = X[j] - r;
|
|
264
297
|
};
|
|
265
298
|
const adjacentTerms = (i, eta) => {
|
|
266
299
|
for (let e = off[i]; e < off[i + 1]; e++) {
|
|
@@ -274,7 +307,8 @@ function stressLayout(request, opts = {}) {
|
|
|
274
307
|
for (let i = 0; i < n; i++) {
|
|
275
308
|
order[i] = i;
|
|
276
309
|
}
|
|
277
|
-
|
|
310
|
+
const warm = anySeed ? Math.floor(WARM * iterations) : 0;
|
|
311
|
+
for (let t = warm; t < iterations; t++) {
|
|
278
312
|
const eta = etaMax * Math.exp(-lambda * t);
|
|
279
313
|
for (let k = n - 1; k > 0; k--) {
|
|
280
314
|
const r = Math.floor(rand() * (k + 1));
|
|
@@ -288,15 +322,15 @@ function stressLayout(request, opts = {}) {
|
|
|
288
322
|
for (let w = 0; w < walks; w++) {
|
|
289
323
|
const length = Math.exp(logMin + rand() * (logMax - logMin));
|
|
290
324
|
const j = walk(i, length);
|
|
291
|
-
if (j >= 0 && j !== i) {
|
|
292
|
-
move(i, j,
|
|
325
|
+
if (j >= 0 && j !== i && walked > 0) {
|
|
326
|
+
move(i, j, walked, eta);
|
|
293
327
|
}
|
|
294
328
|
}
|
|
295
329
|
referenceTerm(i, eta);
|
|
296
330
|
}
|
|
297
331
|
}
|
|
298
|
-
const
|
|
299
|
-
const key = (x, y) => Math.floor(x /
|
|
332
|
+
const radius = UNTANGLE_LINKS * link;
|
|
333
|
+
const key = (x, y) => Math.floor(x / radius) * 73856093 ^ Math.floor(y / radius) * 19349663;
|
|
300
334
|
for (let u = 0; u < UNTANGLE_PASSES; u++) {
|
|
301
335
|
const grid = /* @__PURE__ */ new Map();
|
|
302
336
|
for (let i = 0; i < n; i++) {
|
|
@@ -309,8 +343,8 @@ function stressLayout(request, opts = {}) {
|
|
|
309
343
|
}
|
|
310
344
|
}
|
|
311
345
|
for (let i = 0; i < n; i++) {
|
|
312
|
-
const cx = Math.floor(X[i] /
|
|
313
|
-
const cy = Math.floor(Y[i] /
|
|
346
|
+
const cx = Math.floor(X[i] / radius);
|
|
347
|
+
const cy = Math.floor(Y[i] / radius);
|
|
314
348
|
for (let gx = cx - 1; gx <= cx + 1; gx++) {
|
|
315
349
|
for (let gy = cy - 1; gy <= cy + 1; gy++) {
|
|
316
350
|
const list = grid.get(gx * 73856093 ^ gy * 19349663);
|
|
@@ -334,18 +368,18 @@ function stressLayout(request, opts = {}) {
|
|
|
334
368
|
const dx = X[i] - X[j];
|
|
335
369
|
const dy = Y[i] - Y[j];
|
|
336
370
|
const mag = Math.sqrt(dx * dx + dy * dy);
|
|
337
|
-
if (mag >=
|
|
371
|
+
if (mag >= radius) {
|
|
338
372
|
continue;
|
|
339
373
|
}
|
|
340
374
|
let rx;
|
|
341
375
|
let ry;
|
|
342
376
|
if (mag > 1e-6) {
|
|
343
|
-
const r = UNTANGLE_PUSH * (
|
|
377
|
+
const r = UNTANGLE_PUSH * (radius - mag) / (2 * mag);
|
|
344
378
|
rx = r * dx;
|
|
345
379
|
ry = r * dy;
|
|
346
380
|
} else {
|
|
347
|
-
rx = UNTANGLE_PUSH *
|
|
348
|
-
ry = UNTANGLE_PUSH *
|
|
381
|
+
rx = UNTANGLE_PUSH * radius * (rand() - 0.5);
|
|
382
|
+
ry = UNTANGLE_PUSH * radius * (rand() - 0.5);
|
|
349
383
|
}
|
|
350
384
|
X[i] = X[i] + rx;
|
|
351
385
|
Y[i] = Y[i] + ry;
|
|
@@ -374,28 +408,9 @@ function stressLayout(request, opts = {}) {
|
|
|
374
408
|
Y[a + i] = 0.5 * ys[i] + 0.25 * (ys[i - 1] + ys[i + 1]);
|
|
375
409
|
}
|
|
376
410
|
}
|
|
377
|
-
|
|
378
|
-
let cx = 0;
|
|
379
|
-
let cy = 0;
|
|
380
|
-
for (let i = 0; i < m; i++) {
|
|
381
|
-
cx += X[a + i];
|
|
382
|
-
cy += Y[a + i];
|
|
383
|
-
if (i > 0) {
|
|
384
|
-
length += Math.hypot(
|
|
385
|
-
X[a + i] - X[a + i - 1],
|
|
386
|
-
Y[a + i] - Y[a + i - 1]
|
|
387
|
-
);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
cx /= m;
|
|
391
|
-
cy /= m;
|
|
392
|
-
const scale = length > 0 ? per[k] * (m - 1) / length : 1;
|
|
393
|
-
for (let i = 0; i < m; i++) {
|
|
394
|
-
X[a + i] = cx + (X[a + i] - cx) * scale;
|
|
395
|
-
Y[a + i] = cy + (Y[a + i] - cy) * scale;
|
|
396
|
-
}
|
|
411
|
+
respace(X, Y, a, m, per[k]);
|
|
397
412
|
}
|
|
398
|
-
packComponents(
|
|
413
|
+
packComponents(comp, members, X, Y, anySeed, gap);
|
|
399
414
|
for (let k = 0; k < c.ids.length; k++) {
|
|
400
415
|
const points = [];
|
|
401
416
|
for (let i = 0; i < count[k]; i++) {
|
|
@@ -405,99 +420,110 @@ function stressLayout(request, opts = {}) {
|
|
|
405
420
|
}
|
|
406
421
|
return { nodePositions };
|
|
407
422
|
}
|
|
408
|
-
function
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
423
|
+
function respace(X, Y, a, m, per) {
|
|
424
|
+
const arc = new Float64Array(m);
|
|
425
|
+
for (let i = 1; i < m; i++) {
|
|
426
|
+
arc[i] = arc[i - 1] + Math.hypot(X[a + i] - X[a + i - 1], Y[a + i] - Y[a + i - 1]);
|
|
427
|
+
}
|
|
428
|
+
if (!arc[m - 1]) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
const loss = per * (m - 1) - arc[m - 1];
|
|
432
|
+
if (loss > 0) {
|
|
433
|
+
extend(X, Y, a, a + 1, loss / 2);
|
|
434
|
+
extend(X, Y, a + m - 1, a + m - 2, loss / 2);
|
|
435
|
+
for (let i = 1; i < m; i++) {
|
|
436
|
+
arc[i] = arc[i - 1] + Math.hypot(X[a + i] - X[a + i - 1], Y[a + i] - Y[a + i - 1]);
|
|
415
437
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
438
|
+
}
|
|
439
|
+
const total = arc[m - 1];
|
|
440
|
+
const xs = X.slice(a, a + m);
|
|
441
|
+
const ys = Y.slice(a, a + m);
|
|
442
|
+
let seg = 1;
|
|
443
|
+
for (let i = 1; i + 1 < m; i++) {
|
|
444
|
+
const s = total * i / (m - 1);
|
|
445
|
+
while (seg < m - 1 && arc[seg] < s) {
|
|
446
|
+
seg++;
|
|
426
447
|
}
|
|
427
|
-
|
|
448
|
+
const span = arc[seg] - arc[seg - 1];
|
|
449
|
+
const t = span > 0 ? (s - arc[seg - 1]) / span : 0;
|
|
450
|
+
X[a + i] = xs[seg - 1] + (xs[seg] - xs[seg - 1]) * t;
|
|
451
|
+
Y[a + i] = ys[seg - 1] + (ys[seg] - ys[seg - 1]) * t;
|
|
428
452
|
}
|
|
453
|
+
}
|
|
454
|
+
function extend(X, Y, end, next, by) {
|
|
455
|
+
const dx = X[end] - X[next];
|
|
456
|
+
const dy = Y[end] - Y[next];
|
|
457
|
+
const mag = Math.hypot(dx, dy);
|
|
458
|
+
if (mag > 0) {
|
|
459
|
+
X[end] = X[end] + dx / mag * by;
|
|
460
|
+
Y[end] = Y[end] + dy / mag * by;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
function packComponents(comp, members, X, Y, anySeed, gap) {
|
|
429
464
|
if (!anySeed) {
|
|
430
|
-
for (
|
|
431
|
-
rotateToPrincipalAxis(X, Y,
|
|
465
|
+
for (const points of members) {
|
|
466
|
+
rotateToPrincipalAxis(X, Y, points);
|
|
432
467
|
}
|
|
433
468
|
}
|
|
434
|
-
if (
|
|
469
|
+
if (members.length < 2) {
|
|
435
470
|
return;
|
|
436
471
|
}
|
|
437
|
-
const minX = new Float64Array(
|
|
438
|
-
const minY = new Float64Array(
|
|
439
|
-
const maxY = new Float64Array(
|
|
440
|
-
for (let i = 0; i <
|
|
472
|
+
const minX = new Float64Array(members.length).fill(Infinity);
|
|
473
|
+
const minY = new Float64Array(members.length).fill(Infinity);
|
|
474
|
+
const maxY = new Float64Array(members.length).fill(-Infinity);
|
|
475
|
+
for (let i = 0; i < X.length; i++) {
|
|
441
476
|
const k = comp[i];
|
|
442
477
|
minX[k] = Math.min(minX[k], X[i]);
|
|
443
478
|
minY[k] = Math.min(minY[k], Y[i]);
|
|
444
479
|
maxY[k] = Math.max(maxY[k], Y[i]);
|
|
445
480
|
}
|
|
446
|
-
const shift = new Float64Array(
|
|
481
|
+
const shift = new Float64Array(members.length);
|
|
447
482
|
let y = 0;
|
|
448
|
-
for (let k = 0; k <
|
|
483
|
+
for (let k = 0; k < members.length; k++) {
|
|
449
484
|
shift[k] = y - minY[k];
|
|
450
485
|
y += maxY[k] - minY[k] + gap;
|
|
451
486
|
}
|
|
452
|
-
for (let i = 0; i <
|
|
487
|
+
for (let i = 0; i < X.length; i++) {
|
|
453
488
|
const k = comp[i];
|
|
454
489
|
X[i] = X[i] - minX[k];
|
|
455
490
|
Y[i] = Y[i] + shift[k];
|
|
456
491
|
}
|
|
457
492
|
}
|
|
458
|
-
function rotateToPrincipalAxis(X, Y,
|
|
459
|
-
|
|
493
|
+
function rotateToPrincipalAxis(X, Y, points) {
|
|
494
|
+
if (points.length < 3) {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
460
497
|
let cx = 0;
|
|
461
498
|
let cy = 0;
|
|
462
|
-
for (
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
cy += Y[i];
|
|
466
|
-
m++;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
if (m < 3) {
|
|
470
|
-
return;
|
|
499
|
+
for (const i of points) {
|
|
500
|
+
cx += X[i];
|
|
501
|
+
cy += Y[i];
|
|
471
502
|
}
|
|
472
|
-
cx /=
|
|
473
|
-
cy /=
|
|
503
|
+
cx /= points.length;
|
|
504
|
+
cy /= points.length;
|
|
474
505
|
let sxx = 0;
|
|
475
506
|
let syy = 0;
|
|
476
507
|
let sxy = 0;
|
|
477
|
-
for (
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
sxy += dx * dy;
|
|
484
|
-
}
|
|
508
|
+
for (const i of points) {
|
|
509
|
+
const dx = X[i] - cx;
|
|
510
|
+
const dy = Y[i] - cy;
|
|
511
|
+
sxx += dx * dx;
|
|
512
|
+
syy += dy * dy;
|
|
513
|
+
sxy += dx * dy;
|
|
485
514
|
}
|
|
486
515
|
const angle = -0.5 * Math.atan2(2 * sxy, sxx - syy);
|
|
487
516
|
const cos = Math.cos(angle);
|
|
488
517
|
const sin = Math.sin(angle);
|
|
489
|
-
for (
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
Y[i] = cy + sin * dx + cos * dy;
|
|
495
|
-
}
|
|
518
|
+
for (const i of points) {
|
|
519
|
+
const dx = X[i] - cx;
|
|
520
|
+
const dy = Y[i] - cy;
|
|
521
|
+
X[i] = cx + cos * dx - sin * dy;
|
|
522
|
+
Y[i] = cy + sin * dx + cos * dy;
|
|
496
523
|
}
|
|
497
524
|
}
|
|
498
525
|
|
|
499
526
|
export {
|
|
500
|
-
|
|
501
|
-
LAYOUT_ENGINE_VALUES,
|
|
527
|
+
chains,
|
|
502
528
|
stressLayout
|
|
503
529
|
};
|