@jbrowse/bandage-core 6.0.0 → 6.0.2
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-3C2LGJKY.js +108 -0
- package/dist/chunks/chunk-3PLYZLYV.js +513 -0
- package/dist/chunks/chunk-3S3YVOJ7.js +69 -0
- package/dist/chunks/chunk-3WHKR5TY.js +726 -0
- package/dist/chunks/chunk-47DYCZOK.js +115 -0
- package/dist/chunks/chunk-4S47XRYA.js +149 -0
- package/dist/chunks/chunk-527AI7CP.js +122 -0
- package/dist/chunks/chunk-57ZIXHZA.js +90 -0
- package/dist/chunks/chunk-5QT6NPSO.js +40 -0
- package/dist/chunks/chunk-6JEWNXCQ.js +90 -0
- package/dist/chunks/chunk-6Q6HFBJT.js +170 -0
- package/dist/chunks/chunk-6SBUFFTB.js +97 -0
- package/dist/chunks/chunk-6YRRNX2P.js +61 -0
- package/dist/chunks/chunk-72N5DJHZ.js +155 -0
- package/dist/chunks/chunk-73RSLGZT.js +73 -0
- package/dist/chunks/chunk-7CHJKABZ.js +82 -0
- package/dist/chunks/chunk-7XHHVYG7.js +13 -0
- package/dist/chunks/chunk-AB35TJST.js +67 -0
- package/dist/chunks/chunk-AE3UQZY3.js +43 -0
- package/dist/chunks/chunk-BJV2W4KZ.js +257 -0
- package/dist/chunks/chunk-BPLIXMH7.js +52 -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-CPMHA4W5.js +6 -0
- package/dist/chunks/chunk-CR7EGAFE.js +241 -0
- package/dist/chunks/chunk-CZ54VG2H.js +431 -0
- package/dist/chunks/chunk-D3M7SBKL.js +210 -0
- package/dist/chunks/chunk-EJ5OZTCK.js +52 -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-GQ2AOHU4.js +112 -0
- package/dist/chunks/chunk-HB6PDCOU.js +177 -0
- package/dist/chunks/chunk-HCANBJDD.js +326 -0
- package/dist/chunks/chunk-HIJVRRYE.js +174 -0
- package/dist/chunks/chunk-HNQPEMYE.js +297 -0
- package/dist/chunks/chunk-IGVV37VI.js +60 -0
- package/dist/chunks/chunk-IMW3AK27.js +102 -0
- package/dist/chunks/chunk-JKJWSY4V.js +32 -0
- package/dist/chunks/chunk-KCW7L3NV.js +6 -0
- package/dist/chunks/chunk-KQFOCDFY.js +73 -0
- package/dist/chunks/chunk-L3O7AWLI.js +52 -0
- package/dist/chunks/chunk-L5RYUPZI.js +53 -0
- package/dist/chunks/chunk-LOEA5IHZ.js +71 -0
- package/dist/chunks/chunk-LTUWJKHD.js +112 -0
- package/dist/chunks/chunk-LVZZH2NI.js +181 -0
- package/dist/chunks/chunk-MFKORG3F.js +44 -0
- package/dist/chunks/chunk-MHGHYHTL.js +205 -0
- package/dist/chunks/chunk-MI35EUIE.js +76 -0
- package/dist/chunks/chunk-MNUSQCLG.js +29 -0
- package/dist/chunks/chunk-NNODIW4U.js +26 -0
- package/dist/chunks/chunk-P7B3GDAY.js +232 -0
- package/dist/chunks/chunk-PBENCR5L.js +60 -0
- package/dist/chunks/chunk-PUG7LXDF.js +70 -0
- package/dist/chunks/chunk-Q3KCTA4N.js +202 -0
- package/dist/chunks/chunk-QCYM44UL.js +476 -0
- package/dist/chunks/chunk-QYBZZI5H.js +0 -0
- package/dist/chunks/chunk-RAKOB6IV.js +169 -0
- package/dist/chunks/chunk-RVS2QCOX.js +135 -0
- package/dist/chunks/chunk-SDGM243E.js +190 -0
- package/dist/chunks/chunk-ST2IBUAN.js +119 -0
- package/dist/chunks/chunk-SV27AJCC.js +101 -0
- package/dist/chunks/chunk-SZNZ2T6D.js +198 -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-VKB57ZYE.js +218 -0
- package/dist/chunks/chunk-VZQ4FKLR.js +38 -0
- package/dist/chunks/chunk-WOO7QYKB.js +121 -0
- package/dist/chunks/chunk-WSIFAZU7.js +132 -0
- package/dist/chunks/chunk-X32PABB7.js +117 -0
- package/dist/chunks/chunk-YJQ7CT7F.js +23 -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-OHMWSW6U.js +0 -7400
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isBackbone
|
|
3
|
+
} from "./chunk-FYQWPEEY.js";
|
|
4
|
+
|
|
5
|
+
// src/referenceSpan.ts
|
|
6
|
+
var MAX_BACKBONE_HOPS = 4;
|
|
7
|
+
function buildNeighbors(graph) {
|
|
8
|
+
const neighbors = /* @__PURE__ */ new Map();
|
|
9
|
+
function add(from, to) {
|
|
10
|
+
const list = neighbors.get(from);
|
|
11
|
+
if (list) {
|
|
12
|
+
list.push(to);
|
|
13
|
+
} else {
|
|
14
|
+
neighbors.set(from, [to]);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
for (const edge of graph.edges) {
|
|
18
|
+
add(edge.from, edge.to);
|
|
19
|
+
add(edge.to, edge.from);
|
|
20
|
+
}
|
|
21
|
+
return neighbors;
|
|
22
|
+
}
|
|
23
|
+
function flankingSpan({
|
|
24
|
+
nodeId,
|
|
25
|
+
nodeById,
|
|
26
|
+
neighbors
|
|
27
|
+
}) {
|
|
28
|
+
const seen = /* @__PURE__ */ new Set([nodeId]);
|
|
29
|
+
let frontier = [nodeId];
|
|
30
|
+
let minEnd = Infinity;
|
|
31
|
+
let maxStart = -Infinity;
|
|
32
|
+
for (let hop = 0; hop < MAX_BACKBONE_HOPS && frontier.length > 0; hop++) {
|
|
33
|
+
const next = [];
|
|
34
|
+
for (const id of frontier) {
|
|
35
|
+
const adjacent = neighbors.get(id);
|
|
36
|
+
if (adjacent) {
|
|
37
|
+
for (const neighborId of adjacent) {
|
|
38
|
+
if (!seen.has(neighborId)) {
|
|
39
|
+
seen.add(neighborId);
|
|
40
|
+
const neighbor = nodeById.get(neighborId);
|
|
41
|
+
if (neighbor && isBackbone(neighbor)) {
|
|
42
|
+
minEnd = Math.min(minEnd, neighbor.stable.start + neighbor.length);
|
|
43
|
+
maxStart = Math.max(maxStart, neighbor.stable.start);
|
|
44
|
+
} else {
|
|
45
|
+
next.push(neighborId);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
frontier = next;
|
|
52
|
+
}
|
|
53
|
+
return minEnd === Infinity ? void 0 : {
|
|
54
|
+
start: Math.min(minEnd, maxStart),
|
|
55
|
+
end: Math.max(minEnd, maxStart)
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function nodeReferenceSpan({
|
|
59
|
+
nodeId,
|
|
60
|
+
nodeById,
|
|
61
|
+
neighbors
|
|
62
|
+
}) {
|
|
63
|
+
const node = nodeById.get(nodeId);
|
|
64
|
+
return node === void 0 ? void 0 : isBackbone(node) ? { start: node.stable.start, end: node.stable.start + node.length } : flankingSpan({ nodeId, nodeById, neighbors });
|
|
65
|
+
}
|
|
66
|
+
function referenceMidpoints(graph) {
|
|
67
|
+
const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
|
|
68
|
+
const neighbors = buildNeighbors(graph);
|
|
69
|
+
const midpoints = /* @__PURE__ */ new Map();
|
|
70
|
+
for (const node of graph.nodes) {
|
|
71
|
+
const span = nodeReferenceSpan({ nodeId: node.id, nodeById, neighbors });
|
|
72
|
+
if (span) {
|
|
73
|
+
midpoints.set(node.id, (span.start + span.end) / 2);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return midpoints;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export {
|
|
80
|
+
buildNeighbors,
|
|
81
|
+
nodeReferenceSpan,
|
|
82
|
+
referenceMidpoints
|
|
83
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
walkRows
|
|
3
|
+
} from "./chunk-SZNZ2T6D.js";
|
|
4
|
+
import {
|
|
5
|
+
ROW_HEIGHT_PX
|
|
6
|
+
} from "./chunk-CPMHA4W5.js";
|
|
7
|
+
import {
|
|
8
|
+
backboneNodes,
|
|
9
|
+
backbonePositions,
|
|
10
|
+
overlapsWindow
|
|
11
|
+
} from "./chunk-FYQWPEEY.js";
|
|
12
|
+
|
|
13
|
+
// src/layout/walkRowLayout.ts
|
|
14
|
+
var READOUT_ROOM = 1.3;
|
|
15
|
+
function walkRowsExtent(walks) {
|
|
16
|
+
let longest = walks.reference.bp;
|
|
17
|
+
for (const row of walks.rows) {
|
|
18
|
+
longest = Math.max(longest, row.bp);
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
maxX: walks.origin + longest * READOUT_ROOM,
|
|
22
|
+
maxY: walks.rows.length * ROW_HEIGHT_PX
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function walkRowLayout(graph, region) {
|
|
26
|
+
const backbone = backboneNodes(graph);
|
|
27
|
+
const walks = walkRows(graph, region);
|
|
28
|
+
if (backbone.length === 0 || !walks) {
|
|
29
|
+
return void 0;
|
|
30
|
+
}
|
|
31
|
+
const overWindow = region ? backbone.filter((n) => overlapsWindow(n, region)) : backbone;
|
|
32
|
+
const nodePositions = backbonePositions(
|
|
33
|
+
overWindow.length > 0 ? overWindow : backbone
|
|
34
|
+
);
|
|
35
|
+
const rowLabels = [
|
|
36
|
+
{ label: walks.reference.label, y: 0 },
|
|
37
|
+
...walks.rows.map((row, i) => ({
|
|
38
|
+
label: row.label,
|
|
39
|
+
y: (i + 1) * ROW_HEIGHT_PX
|
|
40
|
+
}))
|
|
41
|
+
];
|
|
42
|
+
return {
|
|
43
|
+
nodePositions,
|
|
44
|
+
rowLabels,
|
|
45
|
+
referenceAxis: true,
|
|
46
|
+
pixelRows: true,
|
|
47
|
+
extent: walkRowsExtent(walks)
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
walkRowsExtent,
|
|
53
|
+
walkRowLayout
|
|
54
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// src/anchoredNodes.ts
|
|
2
|
+
var REFERENCE_RANK = 0;
|
|
3
|
+
function isAnchored(node) {
|
|
4
|
+
return node.stable !== void 0;
|
|
5
|
+
}
|
|
6
|
+
function isBackbone(node) {
|
|
7
|
+
return node.stable?.rank === REFERENCE_RANK;
|
|
8
|
+
}
|
|
9
|
+
function isOffReference(node) {
|
|
10
|
+
return node.stable !== void 0 && node.stable.rank > REFERENCE_RANK;
|
|
11
|
+
}
|
|
12
|
+
function backboneNodes(graph) {
|
|
13
|
+
return graph.nodes.filter(isBackbone);
|
|
14
|
+
}
|
|
15
|
+
function overlapsWindow(node, window) {
|
|
16
|
+
return node.stable.start < window.end && node.stable.start + node.length > window.start;
|
|
17
|
+
}
|
|
18
|
+
function backbonePositions(backbone) {
|
|
19
|
+
const positions = {};
|
|
20
|
+
for (const node of backbone) {
|
|
21
|
+
positions[node.id] = [
|
|
22
|
+
{ x: node.stable.start, y: 0 },
|
|
23
|
+
{ x: node.stable.start + node.length, y: 0 }
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
return positions;
|
|
27
|
+
}
|
|
28
|
+
function firstNodeAtOrAfter(nodes, bp) {
|
|
29
|
+
let lo = 0;
|
|
30
|
+
let hi = nodes.length;
|
|
31
|
+
while (lo < hi) {
|
|
32
|
+
const mid = lo + hi >> 1;
|
|
33
|
+
if (nodes[mid].stable.start < bp) {
|
|
34
|
+
lo = mid + 1;
|
|
35
|
+
} else {
|
|
36
|
+
hi = mid;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return lo;
|
|
40
|
+
}
|
|
41
|
+
function backboneSpan(backbone) {
|
|
42
|
+
let min = Infinity;
|
|
43
|
+
let max = -Infinity;
|
|
44
|
+
for (const node of backbone) {
|
|
45
|
+
min = Math.min(min, node.stable.start);
|
|
46
|
+
max = Math.max(max, node.stable.start + node.length);
|
|
47
|
+
}
|
|
48
|
+
return max - min;
|
|
49
|
+
}
|
|
50
|
+
function referenceSpan(backbone, region) {
|
|
51
|
+
return region && region.end > region.start ? region.end - region.start : backboneSpan(backbone);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export {
|
|
55
|
+
REFERENCE_RANK,
|
|
56
|
+
isAnchored,
|
|
57
|
+
isBackbone,
|
|
58
|
+
isOffReference,
|
|
59
|
+
backboneNodes,
|
|
60
|
+
overlapsWindow,
|
|
61
|
+
backbonePositions,
|
|
62
|
+
firstNodeAtOrAfter,
|
|
63
|
+
backboneSpan,
|
|
64
|
+
referenceSpan
|
|
65
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isBackbone
|
|
3
|
+
} from "./chunk-FYQWPEEY.js";
|
|
4
|
+
|
|
5
|
+
// src/tubeMap/connectors.ts
|
|
6
|
+
function referenceNodes(graph, layout) {
|
|
7
|
+
const nodeById = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
8
|
+
const out = [];
|
|
9
|
+
layout.nodes.forEach((node) => {
|
|
10
|
+
const graphNode = nodeById.get(node.name);
|
|
11
|
+
if (node.order >= 0 && graphNode && isBackbone(graphNode)) {
|
|
12
|
+
out.push({
|
|
13
|
+
node: node.name,
|
|
14
|
+
bp0: graphNode.stable.start,
|
|
15
|
+
bp1: graphNode.stable.start + node.sequenceLength,
|
|
16
|
+
x0: node.x,
|
|
17
|
+
x1: node.x + node.pixelWidth
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
function tubeMapConnectors(nodes, bpToScreen, tubeToScreen, width) {
|
|
24
|
+
const out = [];
|
|
25
|
+
for (const n of nodes) {
|
|
26
|
+
const bottom0 = tubeToScreen(n.x0);
|
|
27
|
+
const bottom1 = tubeToScreen(n.x1);
|
|
28
|
+
if (bottom1 >= 0 && bottom0 <= width) {
|
|
29
|
+
out.push({
|
|
30
|
+
node: n.node,
|
|
31
|
+
top0: bpToScreen(n.bp0),
|
|
32
|
+
top1: bpToScreen(n.bp1),
|
|
33
|
+
bottom0,
|
|
34
|
+
bottom1
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
var HIT_SLOP_PX = 3;
|
|
41
|
+
function connectorAt(connectors, zoneBottom, sx, sy) {
|
|
42
|
+
if (zoneBottom <= 0 || sy < 0 || sy > zoneBottom) {
|
|
43
|
+
return void 0;
|
|
44
|
+
}
|
|
45
|
+
const t = sy / zoneBottom;
|
|
46
|
+
let hit;
|
|
47
|
+
let best = HIT_SLOP_PX;
|
|
48
|
+
for (const c of connectors) {
|
|
49
|
+
const a = c.top0 + (c.bottom0 - c.top0) * t;
|
|
50
|
+
const b = c.top1 + (c.bottom1 - c.top1) * t;
|
|
51
|
+
const dist = Math.max(0, Math.min(a, b) - sx, sx - Math.max(a, b));
|
|
52
|
+
if (dist < best) {
|
|
53
|
+
best = dist;
|
|
54
|
+
hit = c.node;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return hit;
|
|
58
|
+
}
|
|
59
|
+
function band(ctx, c, bottom) {
|
|
60
|
+
ctx.moveTo(c.top0, 0);
|
|
61
|
+
ctx.lineTo(c.top1, 0);
|
|
62
|
+
ctx.lineTo(c.bottom1, bottom);
|
|
63
|
+
ctx.lineTo(c.bottom0, bottom);
|
|
64
|
+
ctx.closePath();
|
|
65
|
+
}
|
|
66
|
+
function sides(ctx, c, bottom) {
|
|
67
|
+
ctx.moveTo(c.top0, 0);
|
|
68
|
+
ctx.lineTo(c.bottom0, bottom);
|
|
69
|
+
ctx.moveTo(c.top1, 0);
|
|
70
|
+
ctx.lineTo(c.bottom1, bottom);
|
|
71
|
+
}
|
|
72
|
+
function drawTubeMapConnectors(ctx, connectors, bottom, {
|
|
73
|
+
highlightNode,
|
|
74
|
+
darkMode
|
|
75
|
+
}) {
|
|
76
|
+
if (bottom <= 0 || connectors.length === 0) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const ink = darkMode ? "255,255,255" : "0,0,0";
|
|
80
|
+
ctx.beginPath();
|
|
81
|
+
for (const c of connectors) {
|
|
82
|
+
band(ctx, c, bottom);
|
|
83
|
+
}
|
|
84
|
+
ctx.fillStyle = `rgba(${ink},0.07)`;
|
|
85
|
+
ctx.fill();
|
|
86
|
+
ctx.beginPath();
|
|
87
|
+
for (const c of connectors) {
|
|
88
|
+
sides(ctx, c, bottom);
|
|
89
|
+
}
|
|
90
|
+
ctx.strokeStyle = `rgba(${ink},0.35)`;
|
|
91
|
+
ctx.lineWidth = 0.5;
|
|
92
|
+
ctx.stroke();
|
|
93
|
+
const lit = connectors.find((c) => c.node === highlightNode);
|
|
94
|
+
if (lit) {
|
|
95
|
+
ctx.beginPath();
|
|
96
|
+
band(ctx, lit, bottom);
|
|
97
|
+
ctx.fillStyle = "rgba(255,192,203,0.5)";
|
|
98
|
+
ctx.fill();
|
|
99
|
+
ctx.beginPath();
|
|
100
|
+
sides(ctx, lit, bottom);
|
|
101
|
+
ctx.strokeStyle = "#ff0000";
|
|
102
|
+
ctx.lineWidth = 1;
|
|
103
|
+
ctx.stroke();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export {
|
|
108
|
+
referenceNodes,
|
|
109
|
+
tubeMapConnectors,
|
|
110
|
+
connectorAt,
|
|
111
|
+
drawTubeMapConnectors
|
|
112
|
+
};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import {
|
|
2
|
+
nodesUnderBox
|
|
3
|
+
} from "./chunk-BJV2W4KZ.js";
|
|
4
|
+
import {
|
|
5
|
+
nodeLabelBudget,
|
|
6
|
+
placeSizeLabels,
|
|
7
|
+
rowLabelBox,
|
|
8
|
+
sizeLabelCandidates
|
|
9
|
+
} from "./chunk-HCANBJDD.js";
|
|
10
|
+
import {
|
|
11
|
+
LABEL_CHAR_PX,
|
|
12
|
+
LABEL_PAD,
|
|
13
|
+
LABEL_PX,
|
|
14
|
+
occupancy,
|
|
15
|
+
placeLabels
|
|
16
|
+
} from "./chunk-AB35TJST.js";
|
|
17
|
+
|
|
18
|
+
// src/labelLayout.ts
|
|
19
|
+
var HALO_FACTOR = 3.4;
|
|
20
|
+
var LEGEND_INSET_PX = 6;
|
|
21
|
+
var ROUTE_STACK = 8;
|
|
22
|
+
var GENE_PIN_DROP_PX = 18;
|
|
23
|
+
var GENE_STACK = 2;
|
|
24
|
+
var TOPMOST_BASELINE = LABEL_PX + LABEL_PAD;
|
|
25
|
+
function geneCoverageNote(pin) {
|
|
26
|
+
const length = pin.gene.end - pin.gene.start;
|
|
27
|
+
if (pin.covered >= 0.98) {
|
|
28
|
+
return void 0;
|
|
29
|
+
}
|
|
30
|
+
const [per, unit] = length >= 1e6 ? [1e6, "Mb"] : length >= 1e3 ? [1e3, "kb"] : [1, "bp"];
|
|
31
|
+
const amount = (bp) => +(bp / per).toFixed(per === 1 ? 0 : 1);
|
|
32
|
+
return `${amount(pin.covered * length)} of ${amount(length)} ${unit}`;
|
|
33
|
+
}
|
|
34
|
+
function geneLabelCandidates(pins, screen, contigThickness, nodesUnder) {
|
|
35
|
+
const ink = contigThickness / 2;
|
|
36
|
+
return byExtent(pins, (pin) => pin.gene.end - pin.gene.start).map((pin) => {
|
|
37
|
+
const { x, y } = screen(pin.at);
|
|
38
|
+
const note = geneCoverageNote(pin);
|
|
39
|
+
return {
|
|
40
|
+
item: pin,
|
|
41
|
+
x,
|
|
42
|
+
y: y + contigThickness + GENE_PIN_DROP_PX,
|
|
43
|
+
text: note ? `${pin.gene.name} \xB7 ${note}` : pin.gene.name,
|
|
44
|
+
fallback: note ? `${pin.gene.name} \u2026` : void 0,
|
|
45
|
+
stack: GENE_STACK,
|
|
46
|
+
fits: (box) => [
|
|
47
|
+
...nodesUnder({
|
|
48
|
+
x0: box.x0 - ink,
|
|
49
|
+
x1: box.x1 + ink,
|
|
50
|
+
y0: box.y0 - ink,
|
|
51
|
+
y1: box.y1 + ink
|
|
52
|
+
})
|
|
53
|
+
].every((id) => pin.nodeIds.has(id))
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function byExtent(items, extent) {
|
|
58
|
+
return [...items].sort((a, b) => extent(b) - extent(a));
|
|
59
|
+
}
|
|
60
|
+
function layoutLabels(m) {
|
|
61
|
+
const { paneWidth: width, canvasHeight: height, translateX, translateY } = m;
|
|
62
|
+
const { scaleX, scaleY } = m.axisScale;
|
|
63
|
+
const frame = { width, height };
|
|
64
|
+
const screen = (p) => ({
|
|
65
|
+
x: p.x * scaleX + translateX,
|
|
66
|
+
y: p.y * scaleY + translateY
|
|
67
|
+
});
|
|
68
|
+
const reserved = m.drawnRowLabels.map(
|
|
69
|
+
({ label, y }) => rowLabelBox(label, y * scaleY + translateY)
|
|
70
|
+
);
|
|
71
|
+
const stripPx = m.referenceStripZonePx ?? 0;
|
|
72
|
+
if (stripPx > 0) {
|
|
73
|
+
reserved.push({ x0: 0, x1: width, y0: 0, y1: stripPx });
|
|
74
|
+
}
|
|
75
|
+
if (m.legendSize.width > 0) {
|
|
76
|
+
reserved.push({
|
|
77
|
+
x0: width - LEGEND_INSET_PX - m.legendSize.width,
|
|
78
|
+
x1: width,
|
|
79
|
+
y0: 0,
|
|
80
|
+
y1: stripPx + LEGEND_INSET_PX + m.legendSize.height
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (m.poppedFrom) {
|
|
84
|
+
reserved.push({
|
|
85
|
+
x0: 0,
|
|
86
|
+
x1: 60 + m.poppedFrom.label.length * LABEL_CHAR_PX * 1.2,
|
|
87
|
+
y0: 0,
|
|
88
|
+
y1: 44
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
const take = occupancy(frame, reserved);
|
|
92
|
+
const onRows = m.drawnRowLabels.length > 0;
|
|
93
|
+
const rowsTop = onRows ? Math.min(...m.drawnRowLabels.map((r) => r.y)) : Infinity;
|
|
94
|
+
const halo = m.contigThickness * HALO_FACTOR;
|
|
95
|
+
const byBubble = byExtent(m.bubbleHalos, (h) => h.members);
|
|
96
|
+
const bubbles = placeLabels(
|
|
97
|
+
byBubble.map((h) => {
|
|
98
|
+
const { x, y } = screen({
|
|
99
|
+
x: h.labelAt.x,
|
|
100
|
+
y: Math.min(h.labelAt.y, rowsTop)
|
|
101
|
+
});
|
|
102
|
+
const baseline = y - halo / 2 - 6;
|
|
103
|
+
return {
|
|
104
|
+
item: h,
|
|
105
|
+
x,
|
|
106
|
+
y: onRows ? Math.max(baseline, TOPMOST_BASELINE) : baseline,
|
|
107
|
+
text: h.label
|
|
108
|
+
};
|
|
109
|
+
}),
|
|
110
|
+
frame,
|
|
111
|
+
take
|
|
112
|
+
);
|
|
113
|
+
const genes = placeLabels(
|
|
114
|
+
geneLabelCandidates(
|
|
115
|
+
m.genePins,
|
|
116
|
+
screen,
|
|
117
|
+
m.contigThickness,
|
|
118
|
+
nodesUnderBox(
|
|
119
|
+
m.nodePositions ?? {},
|
|
120
|
+
{ scaleX, scaleY, translateX, translateY },
|
|
121
|
+
m.positionsVersion
|
|
122
|
+
)
|
|
123
|
+
),
|
|
124
|
+
frame,
|
|
125
|
+
take
|
|
126
|
+
);
|
|
127
|
+
const sizeCandidates = m.nodePositions && m.labelsNodeSizes ? sizeLabelCandidates({
|
|
128
|
+
nodePositions: m.nodePositions,
|
|
129
|
+
nodeLengths: m.nodeLengths,
|
|
130
|
+
deletions: m.showDeletionEdges ? m.deletions : [],
|
|
131
|
+
deletionRoutes: m.deletionRoutes,
|
|
132
|
+
stranded: m.stranded,
|
|
133
|
+
alleleDeletions: m.alleleDeletions,
|
|
134
|
+
axis: m.axisScale,
|
|
135
|
+
translateX,
|
|
136
|
+
translateY,
|
|
137
|
+
width,
|
|
138
|
+
height,
|
|
139
|
+
version: m.positionsVersion
|
|
140
|
+
}) : { deletions: [], nodes: [] };
|
|
141
|
+
const deletionLabels = placeSizeLabels(sizeCandidates.deletions, take);
|
|
142
|
+
const routes = placeLabels(
|
|
143
|
+
byBubble.flatMap(
|
|
144
|
+
(h) => h.routes.map((route) => {
|
|
145
|
+
const { x, y } = screen(route.at);
|
|
146
|
+
return {
|
|
147
|
+
item: { halo: h, route },
|
|
148
|
+
x,
|
|
149
|
+
y: y + 4,
|
|
150
|
+
text: route.text,
|
|
151
|
+
stack: ROUTE_STACK
|
|
152
|
+
};
|
|
153
|
+
})
|
|
154
|
+
),
|
|
155
|
+
frame,
|
|
156
|
+
take
|
|
157
|
+
);
|
|
158
|
+
const nodeLabels = placeSizeLabels(
|
|
159
|
+
sizeCandidates.nodes,
|
|
160
|
+
take,
|
|
161
|
+
nodeLabelBudget(width, height)
|
|
162
|
+
);
|
|
163
|
+
return {
|
|
164
|
+
bubbles,
|
|
165
|
+
genes,
|
|
166
|
+
routes,
|
|
167
|
+
sizes: [...deletionLabels, ...nodeLabels]
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export {
|
|
172
|
+
HALO_FACTOR,
|
|
173
|
+
LEGEND_INSET_PX,
|
|
174
|
+
geneCoverageNote,
|
|
175
|
+
geneLabelCandidates,
|
|
176
|
+
layoutLabels
|
|
177
|
+
};
|