@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,726 @@
|
|
|
1
|
+
import {
|
|
2
|
+
fadeAbgr
|
|
3
|
+
} from "./chunk-MNUSQCLG.js";
|
|
4
|
+
import {
|
|
5
|
+
baseEdgeCurves
|
|
6
|
+
} from "./chunk-6YRRNX2P.js";
|
|
7
|
+
import {
|
|
8
|
+
PATH_LIGHTNESS,
|
|
9
|
+
PATH_SATURATION,
|
|
10
|
+
nameHue,
|
|
11
|
+
pathHueAt
|
|
12
|
+
} from "./chunk-7CHJKABZ.js";
|
|
13
|
+
import {
|
|
14
|
+
referenceMidpoints
|
|
15
|
+
} from "./chunk-ES2N6V56.js";
|
|
16
|
+
import {
|
|
17
|
+
meanDepth,
|
|
18
|
+
nodeWidthPx
|
|
19
|
+
} from "./chunk-PBENCR5L.js";
|
|
20
|
+
import {
|
|
21
|
+
dashCurves,
|
|
22
|
+
pathRibbonOffsets,
|
|
23
|
+
strandSides,
|
|
24
|
+
translateCurves,
|
|
25
|
+
yToXOf
|
|
26
|
+
} from "./chunk-3PLYZLYV.js";
|
|
27
|
+
|
|
28
|
+
// src/renderer/GeometryBuilder.ts
|
|
29
|
+
import { abgrToCssRgba, packAbgr } from "@jbrowse/core/util/colorBits";
|
|
30
|
+
var EDGE_DEFAULT_COLOR = packAbgr(119, 119, 119, 217);
|
|
31
|
+
var EDGE_PATH_FALLBACK_COLOR = packAbgr(136, 136, 136, 217);
|
|
32
|
+
var EDGE_DELETION_COLOR = packAbgr(24, 24, 28, 240);
|
|
33
|
+
var DELETION_THICKNESS_FACTOR = 2.2;
|
|
34
|
+
var LIFT_BACKDROP_COLOR = packAbgr(160, 160, 160, 255);
|
|
35
|
+
var FADED_ALPHA = 0.18;
|
|
36
|
+
var LIFT_BACKDROP_CSS = abgrToCssRgba(
|
|
37
|
+
fadeAbgr(LIFT_BACKDROP_COLOR, FADED_ALPHA)
|
|
38
|
+
);
|
|
39
|
+
var DELETION_DASH_PX = 11;
|
|
40
|
+
var MIN_PATH_STRIPE_PX = 1.2;
|
|
41
|
+
var MIN_WALK_LANE_PX = 4;
|
|
42
|
+
var MIN_SCALE_FOR_OFFSET = 1e-6;
|
|
43
|
+
var ARROW_LENGTH_BASE_PX = 7;
|
|
44
|
+
var ARROW_LENGTH_PER_EDGE_PX = 3;
|
|
45
|
+
var ARROW_HALF_WIDTH_BASE_PX = 3;
|
|
46
|
+
var ARROW_HALF_WIDTH_PER_EDGE_PX = 1.5;
|
|
47
|
+
var MIN_LINK_HEADS = 3;
|
|
48
|
+
var MIN_ARROW_FIT = 0.5;
|
|
49
|
+
var MIN_ARROW_SCALE = 0.45;
|
|
50
|
+
var ARROW_CURVE_STEPS = 12;
|
|
51
|
+
var ABUTTING_PX = 1e-6;
|
|
52
|
+
var walkX = new Float64Array(64);
|
|
53
|
+
var walkY = new Float64Array(64);
|
|
54
|
+
var walkPx = new Float64Array(64);
|
|
55
|
+
function backFromEnd(curves, scale, yToX, untilPx) {
|
|
56
|
+
const most = curves.length * ARROW_CURVE_STEPS + 1;
|
|
57
|
+
if (walkX.length < most) {
|
|
58
|
+
walkX = new Float64Array(most);
|
|
59
|
+
walkY = new Float64Array(most);
|
|
60
|
+
walkPx = new Float64Array(most);
|
|
61
|
+
}
|
|
62
|
+
const last = curves[curves.length - 1];
|
|
63
|
+
walkX[0] = last.x1;
|
|
64
|
+
walkY[0] = last.y1;
|
|
65
|
+
walkPx[0] = 0;
|
|
66
|
+
let n = 1;
|
|
67
|
+
let px = 0;
|
|
68
|
+
for (let ci = curves.length - 1; ci >= 0 && px < untilPx; ci--) {
|
|
69
|
+
const c = curves[ci];
|
|
70
|
+
for (let k = 1; k <= ARROW_CURVE_STEPS && px < untilPx; k++) {
|
|
71
|
+
const t = 1 - (k / ARROW_CURVE_STEPS) ** 2;
|
|
72
|
+
const u = 1 - t;
|
|
73
|
+
const a = u * u * u;
|
|
74
|
+
const b = 3 * u * u * t;
|
|
75
|
+
const d = 3 * u * t * t;
|
|
76
|
+
const e = t * t * t;
|
|
77
|
+
const x = a * c.x0 + b * c.cx0 + d * c.cx1 + e * c.x1;
|
|
78
|
+
const y = a * c.y0 + b * c.cy0 + d * c.cy1 + e * c.y1;
|
|
79
|
+
px += Math.hypot(x - walkX[n - 1], (y - walkY[n - 1]) * yToX) * scale;
|
|
80
|
+
walkX[n] = x;
|
|
81
|
+
walkY[n] = y;
|
|
82
|
+
walkPx[n] = px;
|
|
83
|
+
n++;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return n;
|
|
87
|
+
}
|
|
88
|
+
function walkPointAt(n, px) {
|
|
89
|
+
let i = 1;
|
|
90
|
+
while (i < n - 1 && walkPx[i] < px) {
|
|
91
|
+
i++;
|
|
92
|
+
}
|
|
93
|
+
const from = walkPx[i - 1];
|
|
94
|
+
const to = walkPx[i];
|
|
95
|
+
const f = to > from ? Math.min(1, Math.max(0, (px - from) / (to - from))) : 0;
|
|
96
|
+
return {
|
|
97
|
+
x: walkX[i - 1] + (walkX[i] - walkX[i - 1]) * f,
|
|
98
|
+
y: walkY[i - 1] + (walkY[i] - walkY[i - 1]) * f
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function arrowheadFor({
|
|
102
|
+
curves,
|
|
103
|
+
edgeHalfWidth,
|
|
104
|
+
nodeHalfWidth,
|
|
105
|
+
scale,
|
|
106
|
+
yToX,
|
|
107
|
+
color,
|
|
108
|
+
fromSegments,
|
|
109
|
+
toSegments
|
|
110
|
+
}) {
|
|
111
|
+
const length = ARROW_LENGTH_BASE_PX + ARROW_LENGTH_PER_EDGE_PX * edgeHalfWidth;
|
|
112
|
+
const n = backFromEnd(
|
|
113
|
+
curves,
|
|
114
|
+
scale,
|
|
115
|
+
yToX,
|
|
116
|
+
nodeHalfWidth + MIN_LINK_HEADS * length
|
|
117
|
+
);
|
|
118
|
+
const lengthPx = walkPx[n - 1];
|
|
119
|
+
const abutting = lengthPx < ABUTTING_PX;
|
|
120
|
+
const inset = Math.min(nodeHalfWidth, lengthPx);
|
|
121
|
+
const fit = abutting ? Math.min(1, polylinePx(fromSegments, scale, yToX) / length) : 1;
|
|
122
|
+
if (abutting ? fit < MIN_ARROW_FIT : lengthPx < MIN_LINK_HEADS * length) {
|
|
123
|
+
return void 0;
|
|
124
|
+
}
|
|
125
|
+
const tip = walkPointAt(n, inset);
|
|
126
|
+
const base = walkPointAt(n, inset + length * fit);
|
|
127
|
+
const dx = tip.x - base.x;
|
|
128
|
+
const dy = (tip.y - base.y) * yToX;
|
|
129
|
+
const last = curves[curves.length - 1];
|
|
130
|
+
return {
|
|
131
|
+
x: tip.x,
|
|
132
|
+
y: tip.y,
|
|
133
|
+
angle: Math.hypot(dx, dy) > 0 ? Math.atan2(dy, dx) : endTangent(last, yToX, intoDirection(last, toSegments)),
|
|
134
|
+
length: length * fit,
|
|
135
|
+
halfWidth: (ARROW_HALF_WIDTH_BASE_PX + ARROW_HALF_WIDTH_PER_EDGE_PX * edgeHalfWidth) * fit,
|
|
136
|
+
color
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function polylinePx(segments, scale, yToX) {
|
|
140
|
+
let length = 0;
|
|
141
|
+
for (let i = 1; i < segments.length; i++) {
|
|
142
|
+
length += Math.hypot(
|
|
143
|
+
segments[i].x - segments[i - 1].x,
|
|
144
|
+
(segments[i].y - segments[i - 1].y) * yToX
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
return length * scale;
|
|
148
|
+
}
|
|
149
|
+
function leavesStart(edge, segments, first, stranded, yToX) {
|
|
150
|
+
if (stranded) {
|
|
151
|
+
return strandSides(edge).from === "start";
|
|
152
|
+
}
|
|
153
|
+
const gap = (p) => Math.hypot(p.x - first.x0, (p.y - first.y0) * yToX);
|
|
154
|
+
return gap(segments[0]) < gap(segments.at(-1));
|
|
155
|
+
}
|
|
156
|
+
function intoDirection(last, toSegments) {
|
|
157
|
+
if (toSegments.length < 2) {
|
|
158
|
+
return void 0;
|
|
159
|
+
}
|
|
160
|
+
const first = toSegments[0];
|
|
161
|
+
const final = toSegments[toSegments.length - 1];
|
|
162
|
+
const atStart = Math.hypot(last.x1 - first.x, last.y1 - first.y) <= Math.hypot(last.x1 - final.x, last.y1 - final.y);
|
|
163
|
+
return atStart ? { from: first, to: toSegments[1] } : { from: final, to: toSegments[toSegments.length - 2] };
|
|
164
|
+
}
|
|
165
|
+
function pointNormalsOf(rawPoints, yToX = 1) {
|
|
166
|
+
const points = yToX === 1 ? rawPoints : rawPoints.map((p) => ({ x: p.x, y: p.y * yToX }));
|
|
167
|
+
const pointNormals = [];
|
|
168
|
+
for (let i = 0; i < points.length; i++) {
|
|
169
|
+
let nx = 0;
|
|
170
|
+
let ny = 0;
|
|
171
|
+
if (i === 0) {
|
|
172
|
+
const dx = points[1].x - points[0].x;
|
|
173
|
+
const dy = points[1].y - points[0].y;
|
|
174
|
+
const len = Math.hypot(dx, dy);
|
|
175
|
+
if (len > 0) {
|
|
176
|
+
nx = -dy / len;
|
|
177
|
+
ny = dx / len;
|
|
178
|
+
}
|
|
179
|
+
} else if (i === points.length - 1) {
|
|
180
|
+
const dx = points[i].x - points[i - 1].x;
|
|
181
|
+
const dy = points[i].y - points[i - 1].y;
|
|
182
|
+
const len = Math.hypot(dx, dy);
|
|
183
|
+
if (len > 0) {
|
|
184
|
+
nx = -dy / len;
|
|
185
|
+
ny = dx / len;
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
const dx1 = points[i].x - points[i - 1].x;
|
|
189
|
+
const dy1 = points[i].y - points[i - 1].y;
|
|
190
|
+
const len1 = Math.hypot(dx1, dy1);
|
|
191
|
+
const dx2 = points[i + 1].x - points[i].x;
|
|
192
|
+
const dy2 = points[i + 1].y - points[i].y;
|
|
193
|
+
const len2 = Math.hypot(dx2, dy2);
|
|
194
|
+
if (len1 > 0 && len2 > 0) {
|
|
195
|
+
const nx1 = -dy1 / len1;
|
|
196
|
+
const ny1 = dx1 / len1;
|
|
197
|
+
const nx2 = -dy2 / len2;
|
|
198
|
+
const ny2 = dx2 / len2;
|
|
199
|
+
nx = (nx1 + nx2) / 2;
|
|
200
|
+
ny = (ny1 + ny2) / 2;
|
|
201
|
+
const dot = nx1 * nx + ny1 * ny;
|
|
202
|
+
if (dot > 0.1) {
|
|
203
|
+
nx /= dot;
|
|
204
|
+
ny /= dot;
|
|
205
|
+
}
|
|
206
|
+
} else if (len1 > 0) {
|
|
207
|
+
nx = -dy1 / len1;
|
|
208
|
+
ny = dx1 / len1;
|
|
209
|
+
} else if (len2 > 0) {
|
|
210
|
+
nx = -dy2 / len2;
|
|
211
|
+
ny = dx2 / len2;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
pointNormals.push({ nx, ny });
|
|
215
|
+
}
|
|
216
|
+
return pointNormals;
|
|
217
|
+
}
|
|
218
|
+
function offsetPolyline(points, normals, distance, yToX = 1) {
|
|
219
|
+
return points.map((p, i) => ({
|
|
220
|
+
x: p.x + normals[i].nx * distance,
|
|
221
|
+
y: p.y + normals[i].ny * distance / yToX
|
|
222
|
+
}));
|
|
223
|
+
}
|
|
224
|
+
function packNorm(r, g, b, a) {
|
|
225
|
+
return packAbgr(
|
|
226
|
+
Math.round(r * 255),
|
|
227
|
+
Math.round(g * 255),
|
|
228
|
+
Math.round(b * 255),
|
|
229
|
+
Math.round(a * 255)
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
function hslToRgb(h, s, l) {
|
|
233
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
234
|
+
const x = c * (1 - Math.abs(h / 60 % 2 - 1));
|
|
235
|
+
const m = l - c / 2;
|
|
236
|
+
let r = 0;
|
|
237
|
+
let g = 0;
|
|
238
|
+
let b = 0;
|
|
239
|
+
if (h < 60) {
|
|
240
|
+
r = c;
|
|
241
|
+
g = x;
|
|
242
|
+
} else if (h < 120) {
|
|
243
|
+
r = x;
|
|
244
|
+
g = c;
|
|
245
|
+
} else if (h < 180) {
|
|
246
|
+
g = c;
|
|
247
|
+
b = x;
|
|
248
|
+
} else if (h < 240) {
|
|
249
|
+
g = x;
|
|
250
|
+
b = c;
|
|
251
|
+
} else if (h < 300) {
|
|
252
|
+
r = x;
|
|
253
|
+
b = c;
|
|
254
|
+
} else {
|
|
255
|
+
r = c;
|
|
256
|
+
b = x;
|
|
257
|
+
}
|
|
258
|
+
return [r + m, g + m, b + m];
|
|
259
|
+
}
|
|
260
|
+
var DEPTH_GRADIENT = [
|
|
261
|
+
[68, 1, 84],
|
|
262
|
+
[59, 82, 139],
|
|
263
|
+
[33, 145, 140],
|
|
264
|
+
[94, 201, 98],
|
|
265
|
+
[253, 231, 37]
|
|
266
|
+
];
|
|
267
|
+
var STABLE_RANK_GRADIENT = [
|
|
268
|
+
[237, 137, 44],
|
|
269
|
+
[158, 42, 122]
|
|
270
|
+
];
|
|
271
|
+
var NODE_LENGTH_GRADIENT = [
|
|
272
|
+
[220, 50, 50],
|
|
273
|
+
[50, 120, 220]
|
|
274
|
+
];
|
|
275
|
+
function sampleGradient(stops, t) {
|
|
276
|
+
const pos = Math.max(0, Math.min(1, t)) * (stops.length - 1);
|
|
277
|
+
const i = Math.min(stops.length - 2, Math.floor(pos));
|
|
278
|
+
const f = pos - i;
|
|
279
|
+
const a = stops[i];
|
|
280
|
+
const b = stops[i + 1];
|
|
281
|
+
return packAbgr(
|
|
282
|
+
Math.round(a[0] + (b[0] - a[0]) * f),
|
|
283
|
+
Math.round(a[1] + (b[1] - a[1]) * f),
|
|
284
|
+
Math.round(a[2] + (b[2] - a[2]) * f),
|
|
285
|
+
255
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
function hashColor(str, alpha) {
|
|
289
|
+
const [r, g, b] = hslToRgb(nameHue(str), PATH_SATURATION, PATH_LIGHTNESS);
|
|
290
|
+
return packNorm(r, g, b, alpha);
|
|
291
|
+
}
|
|
292
|
+
var REFERENCE_RAMP_MAX_HUE = 300;
|
|
293
|
+
var REFERENCE_RAMP_SATURATION = 0.7;
|
|
294
|
+
var REFERENCE_RAMP_LIGHTNESS = 0.5;
|
|
295
|
+
var REFERENCE_RAMP_ALT_RGB = [60, 65, 72];
|
|
296
|
+
var REFERENCE_RAMP_ALT_COLOR = packAbgr(...REFERENCE_RAMP_ALT_RGB, 255);
|
|
297
|
+
var REFERENCE_RAMP_ALT_CSS = `rgb(${REFERENCE_RAMP_ALT_RGB.join(", ")})`;
|
|
298
|
+
function computeReferenceRamp(graph, domain) {
|
|
299
|
+
const midpoints = referenceMidpoints(graph);
|
|
300
|
+
if (domain && domain.end > domain.start) {
|
|
301
|
+
return {
|
|
302
|
+
start: domain.start,
|
|
303
|
+
span: domain.end - domain.start,
|
|
304
|
+
midpoints
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
let min = Infinity;
|
|
308
|
+
let max = -Infinity;
|
|
309
|
+
for (const mid of midpoints.values()) {
|
|
310
|
+
min = Math.min(min, mid);
|
|
311
|
+
max = Math.max(max, mid);
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
start: min === Infinity ? 0 : min,
|
|
315
|
+
span: max > min ? max - min : 1,
|
|
316
|
+
midpoints
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
function computeColorSchemeRange(graph) {
|
|
320
|
+
let minDepth = Infinity;
|
|
321
|
+
let maxDepth = -Infinity;
|
|
322
|
+
let minLength = Infinity;
|
|
323
|
+
let maxLength = -Infinity;
|
|
324
|
+
let maxRank = 0;
|
|
325
|
+
for (const n of graph.nodes) {
|
|
326
|
+
if (n.stable !== void 0 && n.stable.rank > maxRank) {
|
|
327
|
+
maxRank = n.stable.rank;
|
|
328
|
+
}
|
|
329
|
+
if (n.depth < minDepth) {
|
|
330
|
+
minDepth = n.depth;
|
|
331
|
+
}
|
|
332
|
+
if (n.depth > maxDepth) {
|
|
333
|
+
maxDepth = n.depth;
|
|
334
|
+
}
|
|
335
|
+
if (n.length < minLength) {
|
|
336
|
+
minLength = n.length;
|
|
337
|
+
}
|
|
338
|
+
if (n.length > maxLength) {
|
|
339
|
+
maxLength = n.length;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return {
|
|
343
|
+
minDepth,
|
|
344
|
+
maxDepth,
|
|
345
|
+
minLength,
|
|
346
|
+
maxLength,
|
|
347
|
+
maxRank,
|
|
348
|
+
nodeCount: graph.nodes.length
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
function getNodeColor(node, nodeIndex, colorScheme, range) {
|
|
352
|
+
switch (colorScheme) {
|
|
353
|
+
case "random":
|
|
354
|
+
return hashColor(node.id, 1);
|
|
355
|
+
case "depth":
|
|
356
|
+
return sampleGradient(
|
|
357
|
+
DEPTH_GRADIENT,
|
|
358
|
+
range.maxDepth > range.minDepth ? (node.depth - range.minDepth) / (range.maxDepth - range.minDepth) : 0.5
|
|
359
|
+
);
|
|
360
|
+
case "node-length":
|
|
361
|
+
return sampleGradient(
|
|
362
|
+
NODE_LENGTH_GRADIENT,
|
|
363
|
+
range.maxLength > range.minLength ? (node.length - range.minLength) / (range.maxLength - range.minLength) : 0.5
|
|
364
|
+
);
|
|
365
|
+
// rGFA states the backbone rather than leaving it to be inferred: rank 0 is
|
|
366
|
+
// the reference, higher ranks are the sequence that diverges from it. A
|
|
367
|
+
// graph with no SR tags draws every node as unranked.
|
|
368
|
+
case "stable-rank":
|
|
369
|
+
return node.stable === void 0 ? packAbgr(160, 160, 160, 255) : node.stable.rank === 0 ? packAbgr(52, 152, 219, 255) : sampleGradient(
|
|
370
|
+
STABLE_RANK_GRADIENT,
|
|
371
|
+
range.maxRank > 1 ? (node.stable.rank - 1) / (range.maxRank - 1) : 0
|
|
372
|
+
);
|
|
373
|
+
// Position on the reference, the one quantity both panels of a
|
|
374
|
+
// graph-over-linear figure can state. A node with no reference position at
|
|
375
|
+
// all — nothing anchored, or an allele whose flanks fell outside the cut —
|
|
376
|
+
// is grey rather than a hue it has not earned.
|
|
377
|
+
case "reference-position": {
|
|
378
|
+
const ramp = range.referenceRamp;
|
|
379
|
+
const mid = ramp?.midpoints.get(node.id);
|
|
380
|
+
if (ramp === void 0 || mid === void 0) {
|
|
381
|
+
return packAbgr(160, 160, 160, 255);
|
|
382
|
+
}
|
|
383
|
+
if (node.stable !== void 0 && node.stable.rank > 0) {
|
|
384
|
+
return REFERENCE_RAMP_ALT_COLOR;
|
|
385
|
+
}
|
|
386
|
+
const frac = Math.max(0, Math.min(1, (mid - ramp.start) / ramp.span));
|
|
387
|
+
const [r, g, b] = hslToRgb(
|
|
388
|
+
frac * REFERENCE_RAMP_MAX_HUE,
|
|
389
|
+
REFERENCE_RAMP_SATURATION,
|
|
390
|
+
REFERENCE_RAMP_LIGHTNESS
|
|
391
|
+
);
|
|
392
|
+
return packNorm(r, g, b, 1);
|
|
393
|
+
}
|
|
394
|
+
case "grey":
|
|
395
|
+
return packAbgr(160, 160, 160, 255);
|
|
396
|
+
case "rainbow": {
|
|
397
|
+
const hue = range.nodeCount > 1 ? nodeIndex / range.nodeCount * 360 : 0;
|
|
398
|
+
const [r, g, b] = hslToRgb(hue, 0.75, 0.5);
|
|
399
|
+
return packNorm(r, g, b, 1);
|
|
400
|
+
}
|
|
401
|
+
default:
|
|
402
|
+
return packAbgr(52, 152, 219, 255);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
function endTangent(c, yToX = 1, into) {
|
|
406
|
+
const candidates = [
|
|
407
|
+
[c.cx1, c.cy1],
|
|
408
|
+
[c.cx0, c.cy0],
|
|
409
|
+
[c.x0, c.y0]
|
|
410
|
+
];
|
|
411
|
+
for (const [x, y] of candidates) {
|
|
412
|
+
const dx = c.x1 - x;
|
|
413
|
+
const dy = (c.y1 - y) * yToX;
|
|
414
|
+
if (Math.hypot(dx, dy) > 0) {
|
|
415
|
+
return Math.atan2(dy, dx);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return into ? Math.atan2((into.to.y - into.from.y) * yToX, into.to.x - into.from.x) : 0;
|
|
419
|
+
}
|
|
420
|
+
function isPolylineInBounds(segments, bounds) {
|
|
421
|
+
let hit = false;
|
|
422
|
+
for (let i = 0; i < segments.length - 1 && !hit; i++) {
|
|
423
|
+
const a = segments[i];
|
|
424
|
+
const b = segments[i + 1];
|
|
425
|
+
hit = Math.max(a.x, b.x) >= bounds.minX && Math.min(a.x, b.x) <= bounds.maxX && Math.max(a.y, b.y) >= bounds.minY && Math.min(a.y, b.y) <= bounds.maxY;
|
|
426
|
+
}
|
|
427
|
+
return hit;
|
|
428
|
+
}
|
|
429
|
+
function isBezierInBounds(curves, bounds) {
|
|
430
|
+
for (const c of curves) {
|
|
431
|
+
const cMinX = Math.min(c.x0, c.cx0, c.cx1, c.x1);
|
|
432
|
+
const cMaxX = Math.max(c.x0, c.cx0, c.cx1, c.x1);
|
|
433
|
+
const cMinY = Math.min(c.y0, c.cy0, c.cy1, c.y1);
|
|
434
|
+
const cMaxY = Math.max(c.y0, c.cy0, c.cy1, c.y1);
|
|
435
|
+
if (cMaxX >= bounds.minX && cMinX <= bounds.maxX && cMaxY >= bounds.minY && cMinY <= bounds.maxY) {
|
|
436
|
+
return true;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
var paletteCache = /* @__PURE__ */ new WeakMap();
|
|
442
|
+
function pathPaletteOf(graph) {
|
|
443
|
+
let palette = paletteCache.get(graph);
|
|
444
|
+
if (!palette) {
|
|
445
|
+
const pathColorByIndex = [];
|
|
446
|
+
const pathIndexByName = /* @__PURE__ */ new Map();
|
|
447
|
+
const paths = graph.paths ?? [];
|
|
448
|
+
for (let i = 0; i < paths.length; i++) {
|
|
449
|
+
const [r, g, b] = hslToRgb(
|
|
450
|
+
pathHueAt(i, paths.length),
|
|
451
|
+
PATH_SATURATION,
|
|
452
|
+
PATH_LIGHTNESS
|
|
453
|
+
);
|
|
454
|
+
pathColorByIndex.push(packNorm(r, g, b, 0.85));
|
|
455
|
+
if (!pathIndexByName.has(paths[i].name)) {
|
|
456
|
+
pathIndexByName.set(paths[i].name, i);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
palette = { pathColorByIndex, pathIndexByName };
|
|
460
|
+
paletteCache.set(graph, palette);
|
|
461
|
+
}
|
|
462
|
+
return palette;
|
|
463
|
+
}
|
|
464
|
+
var slotsCache = /* @__PURE__ */ new WeakMap();
|
|
465
|
+
var NO_SLOTS = /* @__PURE__ */ new Map();
|
|
466
|
+
function pathSlotsOf(graph) {
|
|
467
|
+
let slots = slotsCache.get(graph);
|
|
468
|
+
if (!slots) {
|
|
469
|
+
slots = /* @__PURE__ */ new Map();
|
|
470
|
+
for (const [pathIdx, path] of (graph.paths ?? []).entries()) {
|
|
471
|
+
for (const nodeId of new Set(path.nodeIds)) {
|
|
472
|
+
const visits = slots.get(nodeId);
|
|
473
|
+
if (visits) {
|
|
474
|
+
visits.push(pathIdx);
|
|
475
|
+
} else {
|
|
476
|
+
slots.set(nodeId, [pathIdx]);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
slotsCache.set(graph, slots);
|
|
481
|
+
}
|
|
482
|
+
return slots;
|
|
483
|
+
}
|
|
484
|
+
function buildGeometry(options) {
|
|
485
|
+
const {
|
|
486
|
+
nodePositions,
|
|
487
|
+
graph,
|
|
488
|
+
nodeById,
|
|
489
|
+
colorScheme,
|
|
490
|
+
contigThickness,
|
|
491
|
+
connectorThickness,
|
|
492
|
+
drawPaths,
|
|
493
|
+
nodeWidth = "uniform",
|
|
494
|
+
highlight,
|
|
495
|
+
axis,
|
|
496
|
+
linearLayout,
|
|
497
|
+
viewportBounds,
|
|
498
|
+
referenceRamp,
|
|
499
|
+
deletions,
|
|
500
|
+
deletionRoutes,
|
|
501
|
+
stranded,
|
|
502
|
+
hiddenEdges,
|
|
503
|
+
version = 0
|
|
504
|
+
} = options;
|
|
505
|
+
const depthNorm = nodeWidth === "depth" ? meanDepth(graph) : 0;
|
|
506
|
+
const scale = axis.scaleX;
|
|
507
|
+
const yToX = yToXOf(axis);
|
|
508
|
+
const sharedCurves = baseEdgeCurves(
|
|
509
|
+
nodePositions,
|
|
510
|
+
graph,
|
|
511
|
+
axis,
|
|
512
|
+
deletions,
|
|
513
|
+
version,
|
|
514
|
+
deletionRoutes,
|
|
515
|
+
stranded
|
|
516
|
+
);
|
|
517
|
+
const nodeStrokes = [];
|
|
518
|
+
const nodeStrokeRuns = /* @__PURE__ */ new Map();
|
|
519
|
+
const arrows = [];
|
|
520
|
+
const arrowRuns = /* @__PURE__ */ new Map();
|
|
521
|
+
const edgeCurves = [];
|
|
522
|
+
const edgeCurveRuns = /* @__PURE__ */ new Map();
|
|
523
|
+
const colorRange = { ...computeColorSchemeRange(graph), referenceRamp };
|
|
524
|
+
const nodeIndexMap = /* @__PURE__ */ new Map();
|
|
525
|
+
if (colorScheme === "rainbow") {
|
|
526
|
+
for (let i = 0; i < graph.nodes.length; i++) {
|
|
527
|
+
nodeIndexMap.set(graph.nodes[i].id, i);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
const pathCount = graph.paths?.length ?? 0;
|
|
531
|
+
const { pathColorByIndex, pathIndexByName } = pathPaletteOf(graph);
|
|
532
|
+
const pathColorByName = (name) => pathColorByIndex[pathIndexByName.get(name) ?? -1] ?? EDGE_PATH_FALLBACK_COLOR;
|
|
533
|
+
const nodePathSlots = drawPaths && pathCount > 1 ? pathSlotsOf(graph) : NO_SLOTS;
|
|
534
|
+
const showArrows = scale > (linearLayout ? 1 : MIN_ARROW_SCALE);
|
|
535
|
+
for (let ei = 0; ei < graph.edges.length; ei++) {
|
|
536
|
+
const edge = graph.edges[ei];
|
|
537
|
+
const fromSegments = nodePositions[edge.from];
|
|
538
|
+
const toSegments = nodePositions[edge.to];
|
|
539
|
+
const baseCurves = sharedCurves.get(ei);
|
|
540
|
+
if (!fromSegments?.length || !toSegments?.length || !baseCurves || hiddenEdges?.has(ei)) {
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
const numPaths = edge.pathIds?.length ?? 0;
|
|
544
|
+
const bypassed = deletions?.get(ei);
|
|
545
|
+
const isDeletion = bypassed !== void 0;
|
|
546
|
+
const onWalk = highlight?.edgeIndexes.has(ei) ?? false;
|
|
547
|
+
const edgeThickness = connectorThickness / 2 * (isDeletion ? DELETION_THICKNESS_FACTOR : 1);
|
|
548
|
+
const ribbons = drawPaths && numPaths > 0;
|
|
549
|
+
const plainEdgeColor = isDeletion ? EDGE_DELETION_COLOR : EDGE_DEFAULT_COLOR;
|
|
550
|
+
const edgeColor = !highlight || onWalk ? plainEdgeColor : fadeAbgr(plainEdgeColor, FADED_ALPHA);
|
|
551
|
+
const toNode = nodeById.get(edge.to);
|
|
552
|
+
const intoHalfWidth = (toNode ? nodeWidthPx(toNode, contigThickness, nodeWidth, depthNorm) : contigThickness) / 2;
|
|
553
|
+
const buildSingleEdge = (offsetX, offsetY, color, arrowColor) => {
|
|
554
|
+
const curves = offsetX === 0 && offsetY === 0 ? baseCurves : translateCurves(baseCurves, offsetX, offsetY);
|
|
555
|
+
if (viewportBounds && !isBezierInBounds(curves, viewportBounds)) {
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
if (isDeletion) {
|
|
559
|
+
for (const dash of dashCurves(curves, DELETION_DASH_PX / scale)) {
|
|
560
|
+
edgeCurves.push({ curves: dash, thickness: edgeThickness, color });
|
|
561
|
+
}
|
|
562
|
+
} else {
|
|
563
|
+
edgeCurves.push({ curves, thickness: edgeThickness, color });
|
|
564
|
+
}
|
|
565
|
+
const head = showArrows && arrowColor !== void 0 ? arrowheadFor({
|
|
566
|
+
curves,
|
|
567
|
+
edgeHalfWidth: connectorThickness / 2,
|
|
568
|
+
nodeHalfWidth: intoHalfWidth,
|
|
569
|
+
scale,
|
|
570
|
+
yToX,
|
|
571
|
+
color: arrowColor,
|
|
572
|
+
fromSegments,
|
|
573
|
+
toSegments
|
|
574
|
+
}) : void 0;
|
|
575
|
+
if (head) {
|
|
576
|
+
arrows.push(head);
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
const edgeCurveStart = edgeCurves.length;
|
|
580
|
+
const arrowStart = arrows.length;
|
|
581
|
+
const laneWalks = highlight && onWalk && !ribbons ? highlight.walks : void 0;
|
|
582
|
+
if (laneWalks) {
|
|
583
|
+
const fromNode = nodeById.get(edge.from);
|
|
584
|
+
const width = fromNode ? nodeWidthPx(fromNode, contigThickness, nodeWidth, depthNorm) : contigThickness;
|
|
585
|
+
const laneWidth = Math.max(width / laneWalks.length, MIN_WALK_LANE_PX);
|
|
586
|
+
const normals = pointNormalsOf(fromSegments, yToX);
|
|
587
|
+
const { nx, ny } = (leavesStart(
|
|
588
|
+
edge,
|
|
589
|
+
fromSegments,
|
|
590
|
+
baseCurves[0],
|
|
591
|
+
stranded,
|
|
592
|
+
yToX
|
|
593
|
+
) ? normals[0] : normals.at(-1)) ?? {
|
|
594
|
+
nx: 0,
|
|
595
|
+
ny: 0
|
|
596
|
+
};
|
|
597
|
+
const worldPerScreenPx = 1 / Math.max(scale, MIN_SCALE_FOR_OFFSET);
|
|
598
|
+
laneWalks.forEach((walk, lane) => {
|
|
599
|
+
if (!walk.edgeIndexes.has(ei)) {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
const offset = (lane - (laneWalks.length - 1) / 2) * laneWidth * worldPerScreenPx;
|
|
603
|
+
const curves = offset === 0 ? baseCurves : translateCurves(baseCurves, nx * offset, ny * offset / yToX);
|
|
604
|
+
if (viewportBounds && !isBezierInBounds(curves, viewportBounds)) {
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
edgeCurves.push({
|
|
608
|
+
curves,
|
|
609
|
+
thickness: laneWidth / 2,
|
|
610
|
+
color: walk.colors.get(edge.from) ?? walk.colors.get(edge.to) ?? plainEdgeColor
|
|
611
|
+
});
|
|
612
|
+
});
|
|
613
|
+
} else if (!ribbons) {
|
|
614
|
+
buildSingleEdge(0, 0, edgeColor, edgeColor);
|
|
615
|
+
} else {
|
|
616
|
+
const offsets = pathRibbonOffsets(
|
|
617
|
+
fromSegments,
|
|
618
|
+
toSegments,
|
|
619
|
+
numPaths,
|
|
620
|
+
axis
|
|
621
|
+
);
|
|
622
|
+
const arrowRibbon = Math.floor((numPaths - 1) / 2);
|
|
623
|
+
for (let pathIdx = 0; pathIdx < numPaths; pathIdx++) {
|
|
624
|
+
const offset = offsets[pathIdx];
|
|
625
|
+
const ribbonColor = pathColorByName(edge.pathIds[pathIdx]);
|
|
626
|
+
buildSingleEdge(
|
|
627
|
+
offset.x,
|
|
628
|
+
offset.y,
|
|
629
|
+
highlight && !highlight.names.has(edge.pathIds[pathIdx]) ? fadeAbgr(ribbonColor, FADED_ALPHA) : ribbonColor,
|
|
630
|
+
pathIdx === arrowRibbon ? edgeColor : void 0
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
const edgeCurveCount = edgeCurves.length - edgeCurveStart;
|
|
635
|
+
if (edgeCurveCount > 0) {
|
|
636
|
+
edgeCurveRuns.set(ei, { start: edgeCurveStart, count: edgeCurveCount });
|
|
637
|
+
}
|
|
638
|
+
const arrowCount = arrows.length - arrowStart;
|
|
639
|
+
if (arrowCount > 0) {
|
|
640
|
+
arrowRuns.set(ei, { start: arrowStart, count: arrowCount });
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
for (const [nodeId, segments] of Object.entries(nodePositions)) {
|
|
644
|
+
const node = nodeById.get(nodeId);
|
|
645
|
+
if (!node) {
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
if (viewportBounds && !isPolylineInBounds(segments, viewportBounds)) {
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
const faded = highlight !== void 0 && !highlight.nodeIds.has(nodeId);
|
|
652
|
+
const own = faded ? LIFT_BACKDROP_COLOR : getNodeColor(
|
|
653
|
+
node,
|
|
654
|
+
nodeIndexMap.get(nodeId) ?? 0,
|
|
655
|
+
colorScheme,
|
|
656
|
+
colorRange
|
|
657
|
+
);
|
|
658
|
+
const color = faded ? fadeAbgr(own, FADED_ALPHA) : own;
|
|
659
|
+
const width = nodeWidthPx(node, contigThickness, nodeWidth, depthNorm);
|
|
660
|
+
const nodeThickness = width / 2;
|
|
661
|
+
const start = nodeStrokes.length;
|
|
662
|
+
const slots = nodePathSlots.get(nodeId);
|
|
663
|
+
const slotWidth = width / pathCount;
|
|
664
|
+
const drawable = segments.length >= 2;
|
|
665
|
+
const lanes = highlight && !faded && !slots?.length ? highlight.walks : void 0;
|
|
666
|
+
if (drawable && lanes) {
|
|
667
|
+
const laneWidth = Math.max(width / lanes.length, MIN_WALK_LANE_PX);
|
|
668
|
+
const normals = pointNormalsOf(segments, yToX);
|
|
669
|
+
const worldPerScreenPx = 1 / Math.max(scale, MIN_SCALE_FOR_OFFSET);
|
|
670
|
+
lanes.forEach((walk, lane) => {
|
|
671
|
+
const laneColor = walk.colors.get(nodeId);
|
|
672
|
+
if (laneColor === void 0) {
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
const offset = (lane - (lanes.length - 1) / 2) * laneWidth * worldPerScreenPx;
|
|
676
|
+
nodeStrokes.push({
|
|
677
|
+
points: lanes.length === 1 ? segments : offsetPolyline(segments, normals, offset, yToX),
|
|
678
|
+
thickness: laneWidth / 2,
|
|
679
|
+
color: laneColor
|
|
680
|
+
});
|
|
681
|
+
});
|
|
682
|
+
} else if (drawable && slots?.length && slotWidth >= MIN_PATH_STRIPE_PX) {
|
|
683
|
+
const normals = pointNormalsOf(segments, yToX);
|
|
684
|
+
const worldPerScreenPx = 1 / Math.max(scale, MIN_SCALE_FOR_OFFSET);
|
|
685
|
+
for (const slot of slots) {
|
|
686
|
+
const offset = (slot - (pathCount - 1) / 2) * slotWidth * worldPerScreenPx;
|
|
687
|
+
const stripe = pathColorByIndex[slot] ?? EDGE_PATH_FALLBACK_COLOR;
|
|
688
|
+
nodeStrokes.push({
|
|
689
|
+
points: offsetPolyline(segments, normals, offset, yToX),
|
|
690
|
+
thickness: slotWidth / 2,
|
|
691
|
+
// a node off the lifted walk fades whichever way it is painted
|
|
692
|
+
color: faded ? fadeAbgr(stripe, FADED_ALPHA) : stripe
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
} else if (drawable) {
|
|
696
|
+
nodeStrokes.push({ points: segments, thickness: nodeThickness, color });
|
|
697
|
+
}
|
|
698
|
+
const count = nodeStrokes.length - start;
|
|
699
|
+
if (count > 0) {
|
|
700
|
+
nodeStrokeRuns.set(nodeId, { start, count });
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
return {
|
|
704
|
+
nodeStrokes,
|
|
705
|
+
nodeStrokeRuns,
|
|
706
|
+
arrows,
|
|
707
|
+
arrowRuns,
|
|
708
|
+
edgeCurves,
|
|
709
|
+
edgeCurveRuns
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
export {
|
|
714
|
+
LIFT_BACKDROP_COLOR,
|
|
715
|
+
FADED_ALPHA,
|
|
716
|
+
LIFT_BACKDROP_CSS,
|
|
717
|
+
hslToRgb,
|
|
718
|
+
REFERENCE_RAMP_MAX_HUE,
|
|
719
|
+
REFERENCE_RAMP_ALT_COLOR,
|
|
720
|
+
REFERENCE_RAMP_ALT_CSS,
|
|
721
|
+
computeReferenceRamp,
|
|
722
|
+
computeColorSchemeRange,
|
|
723
|
+
getNodeColor,
|
|
724
|
+
endTangent,
|
|
725
|
+
buildGeometry
|
|
726
|
+
};
|