@jbrowse/bandage-core 6.0.1 → 6.0.3
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/README.md +23 -7
- package/dist/alleleProjection/projectAlleles.js +10 -0
- package/dist/anchoredNodes.js +24 -0
- package/dist/bubbleSpreads.js +11 -0
- package/dist/bubbles/bubbleHalos.js +11 -0
- package/dist/bubbles/bubbleLine.js +14 -0
- package/dist/bubbles/bubblesFromGraph.js +10 -0
- package/dist/bubbles/classifyBubble.js +15 -0
- package/dist/bubbles/popBubble.js +9 -0
- package/dist/chunks/chunk-2CVHXFK7.js +530 -0
- package/dist/chunks/chunk-3S3YVOJ7.js +69 -0
- package/dist/chunks/chunk-47DYCZOK.js +115 -0
- package/dist/chunks/chunk-4EMPBO7S.js +117 -0
- package/dist/chunks/chunk-4S47XRYA.js +149 -0
- package/dist/chunks/chunk-5XQDMQXU.js +431 -0
- package/dist/chunks/chunk-6JEWNXCQ.js +90 -0
- package/dist/chunks/chunk-6SBUFFTB.js +97 -0
- package/dist/chunks/chunk-6SXMCK6I.js +476 -0
- package/dist/chunks/chunk-72N5DJHZ.js +155 -0
- package/dist/chunks/chunk-73RSLGZT.js +73 -0
- package/dist/chunks/chunk-7XHHVYG7.js +13 -0
- package/dist/chunks/chunk-AB35TJST.js +67 -0
- package/dist/chunks/chunk-BPLIXMH7.js +52 -0
- package/dist/chunks/chunk-BUBZ32IC.js +101 -0
- package/dist/chunks/chunk-BXEPA4KS.js +179 -0
- package/dist/chunks/chunk-C44YRP5F.js +85 -0
- package/dist/chunks/chunk-CDLPYR5O.js +135 -0
- package/dist/chunks/chunk-CJ2EE4AF.js +40 -0
- package/dist/chunks/chunk-CPMHA4W5.js +6 -0
- package/dist/chunks/chunk-CR7EGAFE.js +241 -0
- package/dist/chunks/chunk-CS3E4ETM.js +202 -0
- package/dist/chunks/chunk-CTSBC2O6.js +90 -0
- package/dist/chunks/chunk-D3M7SBKL.js +210 -0
- package/dist/chunks/chunk-DKOB2OPC.js +177 -0
- package/dist/chunks/chunk-EJ5OZTCK.js +52 -0
- package/dist/chunks/chunk-EK4HGYO7.js +174 -0
- package/dist/chunks/chunk-EPZORFTI.js +23 -0
- package/dist/chunks/chunk-ES2N6V56.js +83 -0
- package/dist/chunks/chunk-EZPBJPK6.js +54 -0
- package/dist/chunks/chunk-FYQWPEEY.js +65 -0
- package/dist/chunks/chunk-GGQRADFS.js +170 -0
- package/dist/chunks/chunk-GOY6REMT.js +43 -0
- package/dist/chunks/chunk-GQ2AOHU4.js +112 -0
- package/dist/chunks/chunk-HNQPEMYE.js +297 -0
- package/dist/chunks/chunk-IGVV37VI.js +60 -0
- package/dist/chunks/chunk-IO2PCPZI.js +218 -0
- package/dist/chunks/chunk-ISEI2FH3.js +108 -0
- package/dist/chunks/chunk-JKJWSY4V.js +32 -0
- package/dist/chunks/chunk-KQFOCDFY.js +73 -0
- package/dist/chunks/chunk-L3O7AWLI.js +52 -0
- package/dist/chunks/chunk-LLJUG2H7.js +6 -0
- package/dist/chunks/chunk-LOEA5IHZ.js +71 -0
- package/dist/chunks/chunk-LTUWJKHD.js +112 -0
- package/dist/chunks/chunk-MFKORG3F.js +44 -0
- package/dist/chunks/chunk-MI35EUIE.js +76 -0
- package/dist/chunks/chunk-MNUSQCLG.js +29 -0
- package/dist/chunks/chunk-MQJUG2D2.js +53 -0
- package/dist/chunks/chunk-MREQ7GIA.js +181 -0
- package/dist/chunks/chunk-N62KBLFC.js +326 -0
- package/dist/chunks/chunk-NNODIW4U.js +26 -0
- package/dist/chunks/chunk-PBENCR5L.js +60 -0
- package/dist/chunks/chunk-PCZ4PTYC.js +726 -0
- package/dist/chunks/chunk-PUG7LXDF.js +70 -0
- package/dist/chunks/chunk-QYBZZI5H.js +0 -0
- package/dist/chunks/chunk-R6ZUK4Y3.js +232 -0
- package/dist/chunks/chunk-RAKOB6IV.js +169 -0
- package/dist/chunks/chunk-ROF2R3RF.js +205 -0
- package/dist/chunks/chunk-RVS2QCOX.js +135 -0
- package/dist/chunks/chunk-ST2IBUAN.js +119 -0
- package/dist/chunks/chunk-SZNZ2T6D.js +198 -0
- package/dist/chunks/chunk-T43W7NKK.js +122 -0
- package/dist/chunks/chunk-TAMCZHLW.js +190 -0
- package/dist/chunks/chunk-TDQLINVI.js +16 -0
- package/dist/chunks/chunk-TEDQJPMI.js +131 -0
- package/dist/chunks/chunk-TZFWUT2C.js +66 -0
- package/dist/chunks/chunk-UVBGRJQX.js +50 -0
- package/dist/chunks/chunk-VHW7FDHU.js +42 -0
- package/dist/chunks/chunk-VZQ4FKLR.js +38 -0
- package/dist/chunks/chunk-WFX3MAKA.js +102 -0
- package/dist/chunks/chunk-WOO7QYKB.js +121 -0
- package/dist/chunks/chunk-WSIFAZU7.js +132 -0
- package/dist/chunks/chunk-X5L37W4J.js +82 -0
- package/dist/chunks/chunk-Y3IOTZJA.js +61 -0
- package/dist/chunks/chunk-YMSSF2RK.js +257 -0
- package/dist/cli/figure.js +84 -17
- package/dist/colorSchemes.js +10 -0
- package/dist/deletionEdges.js +13 -0
- package/dist/el.js +8 -0
- package/dist/facetGrid.js +16 -0
- package/dist/figure.js +51 -0
- package/dist/gaf/parseGaf.js +12 -0
- package/dist/gbzCut.js +19 -0
- package/dist/gbzJoin.js +6 -0
- package/dist/gbzWindow.js +28 -0
- package/dist/genes/exonOutline.js +11 -0
- package/dist/genes/geneFiles.js +14 -0
- package/dist/genes/genePins.js +10 -0
- package/dist/gfa/gfaConverter.js +11 -0
- package/dist/gfa-core/gfaParser.js +8 -0
- package/dist/gfa-core/index.js +9 -0
- package/dist/graphLabels.js +24 -0
- package/dist/index.js +262 -1407
- package/dist/labelLayout.js +24 -0
- package/dist/layout/anchoredLayout.js +11 -0
- package/dist/layout/deletionRoutes.js +10 -0
- package/dist/layout/drawnScale.js +22 -0
- package/dist/layout/mergeRuns.js +15 -0
- package/dist/layout/orderedLayout.js +12 -0
- package/dist/layout/orientToReference.js +7 -0
- package/dist/layout/placeOffReference.js +9 -0
- package/dist/layout/referenceSeeds.js +11 -0
- package/dist/layout/rowSpacing.js +6 -0
- package/dist/layout/sampleRowLayout.js +11 -0
- package/dist/layout/trimToWindow.js +99 -0
- package/dist/layout/tubeMapLayout.js +28 -0
- package/dist/layout/walkRowDraw.js +65 -0
- package/dist/layout/walkRowLayout.js +13 -0
- package/dist/layout/walkRows.js +11 -0
- package/dist/layout/walkStrip.js +40 -0
- package/dist/layoutModes.js +35 -0
- package/dist/loadBandage.js +6 -0
- package/dist/nodeWidths.js +18 -0
- package/dist/overlayLabels.js +18 -0
- package/dist/pansn.js +18 -0
- package/dist/pathAnchoring.js +18 -0
- package/dist/pathColors.js +31 -0
- package/dist/pipeline.js +40 -0
- package/dist/reference.js +24 -0
- package/dist/referenceRampCss.js +29 -0
- package/dist/referenceSpan.js +11 -0
- package/dist/referenceStrip.js +35 -0
- package/dist/renderer/Canvas2DRenderer.js +9 -0
- package/dist/renderer/GeometryBuilder.js +42 -0
- package/dist/renderer/colorBits.js +8 -0
- package/dist/renderer/recordingCanvas.js +44 -0
- package/dist/renderer/svgCanvas.js +6 -0
- package/dist/renderer/types.js +0 -0
- package/dist/tubeMap/axis.js +25 -0
- package/dist/tubeMap/coarsen.js +361 -0
- package/dist/tubeMap/connectors.js +13 -0
- package/dist/tubeMap/deviations.js +85 -0
- package/dist/tubeMap/draw.js +15 -0
- package/dist/tubeMap/frame.js +9 -0
- package/dist/tubeMap/genes.js +20 -0
- package/dist/tubeMap/mismatches.js +6 -0
- package/dist/tubeMap/nodeColors.js +21 -0
- package/dist/tubeMap/reads.js +17 -0
- package/dist/tubeMap/warp.js +8 -0
- package/dist/types/version.d.ts +1 -1
- package/dist/types.js +0 -0
- package/dist/util/SpatialIndex.js +13 -0
- package/dist/util/edgeCurves.js +10 -0
- package/dist/util/geometry.js +39 -0
- package/dist/util/hitDetection.js +20 -0
- package/dist/util/wheelZoom.js +6 -0
- package/dist/version.js +6 -0
- package/dist/viewport.js +33 -0
- package/dist/walkEncoding.js +39 -0
- package/dist/walkHighlight.js +26 -0
- package/dist/walkKey.js +17 -0
- package/package.json +38 -5
- package/dist/chunks/chunk-EXSFRX2F.js +0 -7400
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import {
|
|
2
|
+
occupancy
|
|
3
|
+
} from "./chunk-AB35TJST.js";
|
|
4
|
+
import {
|
|
5
|
+
polylineMidpoint
|
|
6
|
+
} from "./chunk-TAMCZHLW.js";
|
|
7
|
+
import {
|
|
8
|
+
deletionArcCurves
|
|
9
|
+
} from "./chunk-4EMPBO7S.js";
|
|
10
|
+
import {
|
|
11
|
+
curveBounds,
|
|
12
|
+
curveMidpoint
|
|
13
|
+
} from "./chunk-2CVHXFK7.js";
|
|
14
|
+
|
|
15
|
+
// src/graphLabels.ts
|
|
16
|
+
var MIN_DELETION_LABEL_PX = 26;
|
|
17
|
+
var PX_PER_NODE_LABEL = 4e4;
|
|
18
|
+
var MIN_NODE_LABELS = 8;
|
|
19
|
+
var LEADER_GAP_PX = 8;
|
|
20
|
+
var LABEL_CHAR_PX = 5.7;
|
|
21
|
+
var LABEL_PAD_PX = 6;
|
|
22
|
+
var LABEL_HEIGHT_PX = 13;
|
|
23
|
+
var LABEL_GAP_PX = 3;
|
|
24
|
+
var MAX_LABEL_OVERHANG = 2;
|
|
25
|
+
function labelHalfWidth(text) {
|
|
26
|
+
return (text.length * LABEL_CHAR_PX + LABEL_PAD_PX + LABEL_GAP_PX) / 2;
|
|
27
|
+
}
|
|
28
|
+
var LABEL_HALF_HEIGHT = (LABEL_HEIGHT_PX + LABEL_GAP_PX) / 2;
|
|
29
|
+
var MIN_LABEL_HALF_WIDTH = labelHalfWidth("1 bp");
|
|
30
|
+
function boxAt(halfW, x, y) {
|
|
31
|
+
return {
|
|
32
|
+
x0: x - halfW,
|
|
33
|
+
x1: x + halfW,
|
|
34
|
+
y0: y - LABEL_HALF_HEIGHT,
|
|
35
|
+
y1: y + LABEL_HALF_HEIGHT
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function rowLabelBox(text, screenY) {
|
|
39
|
+
return {
|
|
40
|
+
x0: 6,
|
|
41
|
+
x1: 6 + text.length * 6.2 + 8 + LABEL_GAP_PX,
|
|
42
|
+
y0: screenY - 8 - LABEL_GAP_PX / 2,
|
|
43
|
+
y1: screenY + 8 + LABEL_GAP_PX / 2
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function clamp(v, lo, hi) {
|
|
47
|
+
return hi < lo ? v : Math.min(Math.max(v, lo), hi);
|
|
48
|
+
}
|
|
49
|
+
function groupDigits(n) {
|
|
50
|
+
return String(n).replace(/\B(?=(\d{3})+$)/g, ",");
|
|
51
|
+
}
|
|
52
|
+
function formatBp(bp) {
|
|
53
|
+
if (bp >= 1e6) {
|
|
54
|
+
return `${+(bp / 1e6).toFixed(1)} Mb`;
|
|
55
|
+
}
|
|
56
|
+
return bp >= 1e3 ? `${+(bp / 1e3).toFixed(1)} kb` : `${bp} bp`;
|
|
57
|
+
}
|
|
58
|
+
function deletionText(bp) {
|
|
59
|
+
return `${formatBp(bp)} del`;
|
|
60
|
+
}
|
|
61
|
+
function arcOutward(curves, apex, scaleX, scaleY) {
|
|
62
|
+
const first = curves[0];
|
|
63
|
+
const last = curves[curves.length - 1];
|
|
64
|
+
const dx = (apex.x - (first.x0 + last.x1) / 2) * scaleX;
|
|
65
|
+
const dy = (apex.y - (first.y0 + last.y1) / 2) * scaleY;
|
|
66
|
+
const len = Math.hypot(dx, dy);
|
|
67
|
+
return len === 0 ? { x: 0, y: -1 } : { x: dx / len, y: dy / len };
|
|
68
|
+
}
|
|
69
|
+
function boxSupport(box, dir) {
|
|
70
|
+
return Math.abs(dir.x) * (box.x1 - box.x0) / 2 + Math.abs(dir.y) * (box.y1 - box.y0) / 2;
|
|
71
|
+
}
|
|
72
|
+
function boxRayCrossing(box, dir) {
|
|
73
|
+
const halfW = (box.x1 - box.x0) / 2;
|
|
74
|
+
const halfH = (box.y1 - box.y0) / 2;
|
|
75
|
+
return Math.min(
|
|
76
|
+
dir.x === 0 ? Infinity : halfW / Math.abs(dir.x),
|
|
77
|
+
dir.y === 0 ? Infinity : halfH / Math.abs(dir.y)
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
function drawnLength(segments, scaleX, scaleY) {
|
|
81
|
+
let total = 0;
|
|
82
|
+
for (let i = 1; i < segments.length; i++) {
|
|
83
|
+
const a = segments[i - 1];
|
|
84
|
+
const b = segments[i];
|
|
85
|
+
total += Math.hypot((b.x - a.x) * scaleX, (b.y - a.y) * scaleY);
|
|
86
|
+
}
|
|
87
|
+
return total;
|
|
88
|
+
}
|
|
89
|
+
function runBounds(nodeIds, nodePositions) {
|
|
90
|
+
let minX = Infinity;
|
|
91
|
+
let minY = Infinity;
|
|
92
|
+
let maxX = -Infinity;
|
|
93
|
+
let maxY = -Infinity;
|
|
94
|
+
for (const id of nodeIds) {
|
|
95
|
+
for (const p of nodePositions[id] ?? []) {
|
|
96
|
+
minX = Math.min(minX, p.x);
|
|
97
|
+
maxX = Math.max(maxX, p.x);
|
|
98
|
+
minY = Math.min(minY, p.y);
|
|
99
|
+
maxY = Math.max(maxY, p.y);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return minX === Infinity ? void 0 : { minX, minY, maxX, maxY };
|
|
103
|
+
}
|
|
104
|
+
var nodeOrderCache = /* @__PURE__ */ new WeakMap();
|
|
105
|
+
function labelOrder(nodePositions, nodeLengths) {
|
|
106
|
+
const cached = nodeOrderCache.get(nodePositions);
|
|
107
|
+
if (cached?.nodeLengths === nodeLengths) {
|
|
108
|
+
return cached.order;
|
|
109
|
+
}
|
|
110
|
+
const order = Object.entries(nodePositions).filter(([id, segments]) => segments.length > 0 && nodeLengths.has(id)).sort(([a], [b]) => nodeLengths.get(b) - nodeLengths.get(a));
|
|
111
|
+
nodeOrderCache.set(nodePositions, { nodeLengths, order });
|
|
112
|
+
return order;
|
|
113
|
+
}
|
|
114
|
+
var arcCache = /* @__PURE__ */ new WeakMap();
|
|
115
|
+
function arcPlacements(nodePositions, deletions, axis, version, routes, stranded) {
|
|
116
|
+
const { scaleX, scaleY } = axis;
|
|
117
|
+
const cached = arcCache.get(nodePositions);
|
|
118
|
+
if (cached?.deletions === deletions && cached.routes === routes && cached.stranded === stranded && cached.scaleX === scaleX && cached.scaleY === scaleY && cached.version === version) {
|
|
119
|
+
return cached.arcs;
|
|
120
|
+
}
|
|
121
|
+
const arcs = [];
|
|
122
|
+
for (const deletion of [...deletions].sort((a, b) => b.bp - a.bp)) {
|
|
123
|
+
const curves = deletionArcCurves(
|
|
124
|
+
nodePositions,
|
|
125
|
+
deletion,
|
|
126
|
+
axis,
|
|
127
|
+
routes?.[deletion.edgeIndex],
|
|
128
|
+
stranded
|
|
129
|
+
);
|
|
130
|
+
const apex = curves ? curveMidpoint(curves) : void 0;
|
|
131
|
+
if (curves && apex) {
|
|
132
|
+
const { minX, minY, maxX, maxY } = curveBounds(curves);
|
|
133
|
+
arcs.push({
|
|
134
|
+
deletion,
|
|
135
|
+
curves,
|
|
136
|
+
apex,
|
|
137
|
+
// in screen px, so the two axes' units cannot be added by accident
|
|
138
|
+
extent: Math.max((maxX - minX) * scaleX, (maxY - minY) * scaleY)
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
arcCache.set(nodePositions, {
|
|
143
|
+
deletions,
|
|
144
|
+
routes,
|
|
145
|
+
stranded,
|
|
146
|
+
scaleX,
|
|
147
|
+
scaleY,
|
|
148
|
+
version,
|
|
149
|
+
arcs
|
|
150
|
+
});
|
|
151
|
+
return arcs;
|
|
152
|
+
}
|
|
153
|
+
function sizeLabelCandidates({
|
|
154
|
+
nodePositions,
|
|
155
|
+
nodeLengths,
|
|
156
|
+
deletions,
|
|
157
|
+
deletionRoutes,
|
|
158
|
+
stranded = false,
|
|
159
|
+
alleleDeletions,
|
|
160
|
+
axis,
|
|
161
|
+
translateX,
|
|
162
|
+
translateY,
|
|
163
|
+
width,
|
|
164
|
+
height,
|
|
165
|
+
version = 0
|
|
166
|
+
}) {
|
|
167
|
+
const { scaleX, scaleY } = axis;
|
|
168
|
+
const deletionCandidates = [];
|
|
169
|
+
const nodeCandidates = [];
|
|
170
|
+
for (const { deletion, curves, apex, extent } of arcPlacements(
|
|
171
|
+
nodePositions,
|
|
172
|
+
deletions,
|
|
173
|
+
axis,
|
|
174
|
+
version,
|
|
175
|
+
deletionRoutes,
|
|
176
|
+
stranded
|
|
177
|
+
)) {
|
|
178
|
+
if (extent >= MIN_DELETION_LABEL_PX) {
|
|
179
|
+
const text = deletionText(deletion.bp);
|
|
180
|
+
const halfW = labelHalfWidth(text);
|
|
181
|
+
const box = boxAt(halfW, 0, 0);
|
|
182
|
+
const arcX = apex.x * scaleX + translateX;
|
|
183
|
+
const arcY = apex.y * scaleY + translateY;
|
|
184
|
+
const fits = extent >= halfW * 2 / MAX_LABEL_OVERHANG;
|
|
185
|
+
const bow = arcOutward(curves, apex, scaleX, scaleY);
|
|
186
|
+
const offset = boxSupport(box, bow) + LEADER_GAP_PX;
|
|
187
|
+
const x = fits ? arcX : clamp(arcX + bow.x * offset, halfW, width - halfW);
|
|
188
|
+
const y = fits ? arcY : clamp(
|
|
189
|
+
arcY + bow.y * offset,
|
|
190
|
+
LABEL_HALF_HEIGHT,
|
|
191
|
+
height - LABEL_HALF_HEIGHT
|
|
192
|
+
);
|
|
193
|
+
const away = Math.hypot(x - arcX, y - arcY);
|
|
194
|
+
let leader;
|
|
195
|
+
if (away > 0) {
|
|
196
|
+
const dir = { x: (x - arcX) / away, y: (y - arcY) / away };
|
|
197
|
+
const crossing = boxRayCrossing(box, dir);
|
|
198
|
+
leader = {
|
|
199
|
+
arcX,
|
|
200
|
+
arcY,
|
|
201
|
+
labelX: x - dir.x * crossing,
|
|
202
|
+
labelY: y - dir.y * crossing
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
deletionCandidates.push({
|
|
206
|
+
label: {
|
|
207
|
+
key: `del:${deletion.edgeIndex}`,
|
|
208
|
+
text,
|
|
209
|
+
x,
|
|
210
|
+
y,
|
|
211
|
+
kind: "deletion",
|
|
212
|
+
leader
|
|
213
|
+
},
|
|
214
|
+
box: boxAt(halfW, x, y)
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const labelledByDeletion = /* @__PURE__ */ new Set();
|
|
219
|
+
const alleleRuns = [...alleleDeletions ?? []].sort((a, b) => b.bp - a.bp);
|
|
220
|
+
for (const run of alleleRuns) {
|
|
221
|
+
const drawn = runBounds(run.nodeIds, nodePositions);
|
|
222
|
+
if (!drawn) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const shownLeft = Math.max(drawn.minX * scaleX + translateX, 0);
|
|
226
|
+
const shownRight = Math.min(drawn.maxX * scaleX + translateX, width);
|
|
227
|
+
const shownTop = Math.max(drawn.minY * scaleY + translateY, 0);
|
|
228
|
+
const shownBottom = Math.min(drawn.maxY * scaleY + translateY, height);
|
|
229
|
+
if (shownRight < shownLeft || shownBottom < shownTop) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const extent = Math.max(shownRight - shownLeft, shownBottom - shownTop);
|
|
233
|
+
const text = deletionText(run.bp);
|
|
234
|
+
const halfW = labelHalfWidth(text);
|
|
235
|
+
if (extent >= halfW * 2 / MAX_LABEL_OVERHANG) {
|
|
236
|
+
for (const id of run.nodeIds) {
|
|
237
|
+
labelledByDeletion.add(id);
|
|
238
|
+
}
|
|
239
|
+
const x = clamp((shownLeft + shownRight) / 2, halfW, width - halfW);
|
|
240
|
+
const y = clamp(
|
|
241
|
+
(shownTop + shownBottom) / 2,
|
|
242
|
+
LABEL_HALF_HEIGHT,
|
|
243
|
+
height - LABEL_HALF_HEIGHT
|
|
244
|
+
);
|
|
245
|
+
deletionCandidates.push({
|
|
246
|
+
label: {
|
|
247
|
+
key: `alleledel:${run.nodeIds.join(",")}`,
|
|
248
|
+
text,
|
|
249
|
+
x,
|
|
250
|
+
y,
|
|
251
|
+
kind: "deletion"
|
|
252
|
+
},
|
|
253
|
+
box: boxAt(halfW, x, y)
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
for (const [id, segments] of labelOrder(nodePositions, nodeLengths)) {
|
|
258
|
+
if (labelledByDeletion.has(id)) {
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
const { x, y } = polylineMidpoint(segments);
|
|
262
|
+
const screenY = y * scaleY + translateY;
|
|
263
|
+
if (screenY + LABEL_HALF_HEIGHT <= 0 || screenY - LABEL_HALF_HEIGHT >= height) {
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
const drawn = drawnLength(segments, scaleX, scaleY);
|
|
267
|
+
if (drawn < MIN_LABEL_HALF_WIDTH * 2 / MAX_LABEL_OVERHANG) {
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
const text = formatBp(nodeLengths.get(id));
|
|
271
|
+
const halfW = labelHalfWidth(text);
|
|
272
|
+
if (drawn >= halfW * 2 / MAX_LABEL_OVERHANG) {
|
|
273
|
+
const screenX = x * scaleX + translateX;
|
|
274
|
+
nodeCandidates.push({
|
|
275
|
+
label: {
|
|
276
|
+
key: `node:${id}`,
|
|
277
|
+
text,
|
|
278
|
+
x: screenX,
|
|
279
|
+
y: screenY,
|
|
280
|
+
kind: "node"
|
|
281
|
+
},
|
|
282
|
+
box: boxAt(halfW, screenX, screenY)
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return { deletions: deletionCandidates, nodes: nodeCandidates };
|
|
287
|
+
}
|
|
288
|
+
function nodeLabelBudget(width, height) {
|
|
289
|
+
return Math.max(
|
|
290
|
+
MIN_NODE_LABELS,
|
|
291
|
+
Math.floor(width * height / PX_PER_NODE_LABEL)
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
function placeSizeLabels(candidates, take, budget = Infinity) {
|
|
295
|
+
const labels = [];
|
|
296
|
+
for (const { label, box } of candidates) {
|
|
297
|
+
if (labels.length >= budget) {
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
if (take(box)) {
|
|
301
|
+
labels.push(label);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return labels;
|
|
305
|
+
}
|
|
306
|
+
function graphLabels({
|
|
307
|
+
reserved,
|
|
308
|
+
...args
|
|
309
|
+
}) {
|
|
310
|
+
const take = occupancy(args, reserved);
|
|
311
|
+
const { deletions, nodes } = sizeLabelCandidates(args);
|
|
312
|
+
return [
|
|
313
|
+
...placeSizeLabels(deletions, take),
|
|
314
|
+
...placeSizeLabels(nodes, take, nodeLabelBudget(args.width, args.height))
|
|
315
|
+
];
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export {
|
|
319
|
+
rowLabelBox,
|
|
320
|
+
groupDigits,
|
|
321
|
+
formatBp,
|
|
322
|
+
sizeLabelCandidates,
|
|
323
|
+
nodeLabelBudget,
|
|
324
|
+
placeSizeLabels,
|
|
325
|
+
graphLabels
|
|
326
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// src/el.ts
|
|
2
|
+
function el(tag, attrs = {}, ...children) {
|
|
3
|
+
return {
|
|
4
|
+
tag,
|
|
5
|
+
attrs,
|
|
6
|
+
children: children.filter((c) => !!c || c === "")
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
function escapeText(s) {
|
|
10
|
+
return s.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
11
|
+
}
|
|
12
|
+
function escapeAttr(s) {
|
|
13
|
+
return escapeText(s).replaceAll('"', """);
|
|
14
|
+
}
|
|
15
|
+
function serializeEl(node) {
|
|
16
|
+
if (typeof node === "string") {
|
|
17
|
+
return escapeText(node);
|
|
18
|
+
}
|
|
19
|
+
const attrs = Object.entries(node.attrs).filter(([, v]) => v !== void 0).map(([k, v]) => ` ${k}="${escapeAttr(String(v))}"`).join("");
|
|
20
|
+
return `<${node.tag}${attrs}>${node.children.map(serializeEl).join("")}</${node.tag}>`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
el,
|
|
25
|
+
serializeEl
|
|
26
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// src/nodeWidths.ts
|
|
2
|
+
var NODE_WIDTHS = [
|
|
3
|
+
{
|
|
4
|
+
value: "depth",
|
|
5
|
+
label: "By depth",
|
|
6
|
+
description: "Thicker the more paths carry a node, on the square root of its depth against the mean. A graph with one depth everywhere draws uniform."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
value: "uniform",
|
|
10
|
+
label: "Uniform",
|
|
11
|
+
description: "Every node the same thickness."
|
|
12
|
+
}
|
|
13
|
+
];
|
|
14
|
+
var NODE_WIDTH_VALUES = NODE_WIDTHS.map((w) => w.value);
|
|
15
|
+
var MIN_FACTOR = 0.3;
|
|
16
|
+
var MAX_FACTOR = 3;
|
|
17
|
+
function meanDepth(graph) {
|
|
18
|
+
let weighted = 0;
|
|
19
|
+
let length = 0;
|
|
20
|
+
for (const node of graph.nodes) {
|
|
21
|
+
weighted += node.depth * node.length;
|
|
22
|
+
length += node.length;
|
|
23
|
+
}
|
|
24
|
+
return length > 0 ? weighted / length : 0;
|
|
25
|
+
}
|
|
26
|
+
function depthWidthFactor(node, mean) {
|
|
27
|
+
if (!(mean > 0) || !(node.depth > 0)) {
|
|
28
|
+
return 1;
|
|
29
|
+
}
|
|
30
|
+
return Math.min(
|
|
31
|
+
MAX_FACTOR,
|
|
32
|
+
Math.max(MIN_FACTOR, Math.sqrt(node.depth / mean))
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
function nodeWidthPx(node, contigThickness, nodeWidth, mean) {
|
|
36
|
+
return contigThickness * (nodeWidth === "depth" ? depthWidthFactor(node, mean) : 1);
|
|
37
|
+
}
|
|
38
|
+
function maxNodeWidthPx(contigThickness, nodeWidth) {
|
|
39
|
+
return contigThickness * (nodeWidth === "depth" ? MAX_FACTOR : 1);
|
|
40
|
+
}
|
|
41
|
+
function nodeInk(graph, nodeById, contigThickness, nodeWidth) {
|
|
42
|
+
const mean = graph ? meanDepth(graph) : 0;
|
|
43
|
+
return {
|
|
44
|
+
maxHalfWidthPx: maxNodeWidthPx(contigThickness, nodeWidth) / 2,
|
|
45
|
+
halfWidthPx: (id) => {
|
|
46
|
+
const node = nodeById?.get(id);
|
|
47
|
+
return node ? nodeWidthPx(node, contigThickness, nodeWidth, mean) / 2 : contigThickness / 2;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export {
|
|
53
|
+
NODE_WIDTHS,
|
|
54
|
+
NODE_WIDTH_VALUES,
|
|
55
|
+
meanDepth,
|
|
56
|
+
depthWidthFactor,
|
|
57
|
+
nodeWidthPx,
|
|
58
|
+
maxNodeWidthPx,
|
|
59
|
+
nodeInk
|
|
60
|
+
};
|