@jbrowse/bandage-core 10.2.2 → 10.2.4

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.
Files changed (140) hide show
  1. package/README.md +8 -7
  2. package/dist/bubbles/bubbleHalos.js +7 -3
  3. package/dist/bubbles/bubblesFromGraph.js +4 -2
  4. package/dist/bubbles/classifyBubble.js +10 -5
  5. package/dist/bubbles/superbubbles.js +10 -6
  6. package/dist/chunks/{chunk-N6FECAXP.js → chunk-24WITXQY.js} +17 -8
  7. package/dist/chunks/{chunk-EYK7DJZV.js → chunk-2CHS5LMM.js} +1 -1
  8. package/dist/chunks/{chunk-XTCNEKO5.js → chunk-2QLLQJ72.js} +15 -13
  9. package/dist/chunks/{chunk-TWRLC3KK.js → chunk-3XSM4OZU.js} +1 -1
  10. package/dist/chunks/{chunk-WEDZ76AK.js → chunk-4DRQTBBE.js} +1 -1
  11. package/dist/chunks/{chunk-2LQ5BWT7.js → chunk-4ETPXDO6.js} +1 -1
  12. package/dist/chunks/{chunk-FP4H7SFT.js → chunk-4HWM3FNK.js} +2 -2
  13. package/dist/chunks/{chunk-C7MZJSPT.js → chunk-54POIBVC.js} +2 -8
  14. package/dist/chunks/{chunk-PKFMP65V.js → chunk-5GMFEPWR.js} +1 -1
  15. package/dist/chunks/{chunk-CDLPYR5O.js → chunk-5I3K4R2G.js} +7 -6
  16. package/dist/chunks/{chunk-2CVHXFK7.js → chunk-7SRWJLSC.js} +19 -22
  17. package/dist/chunks/{chunk-SBMJNRTV.js → chunk-A4PFFC4Q.js} +37 -27
  18. package/dist/chunks/{chunk-FNGIQF43.js → chunk-ABUIN5AK.js} +6 -14
  19. package/dist/chunks/{chunk-IW6CYVX2.js → chunk-AR4BUBMU.js} +6 -1
  20. package/dist/chunks/{chunk-IUDJKOLB.js → chunk-B6IW3IRE.js} +15 -33
  21. package/dist/chunks/{chunk-JUIJI7RC.js → chunk-BAETMD3J.js} +9 -4
  22. package/dist/chunks/{chunk-L5WF4ZPS.js → chunk-BS4UZ44I.js} +1 -1
  23. package/dist/chunks/{chunk-HNQPEMYE.js → chunk-CM6LO6OC.js} +0 -1
  24. package/dist/chunks/{chunk-CLOP7UVD.js → chunk-CQHUZHET.js} +11 -8
  25. package/dist/chunks/{chunk-R456ME6J.js → chunk-CU6P5RUG.js} +17 -6
  26. package/dist/chunks/chunk-D5D36ZF6.js +107 -0
  27. package/dist/chunks/{chunk-AFP3NWSL.js → chunk-DY3JCAMX.js} +1 -1
  28. package/dist/chunks/{chunk-NRV7R36Q.js → chunk-EFQLEGWM.js} +3 -3
  29. package/dist/chunks/{chunk-BXEPA4KS.js → chunk-G6UIYQZR.js} +5 -2
  30. package/dist/chunks/{chunk-ASSNZXCF.js → chunk-GINYTQ43.js} +44 -103
  31. package/dist/chunks/{chunk-NNODIW4U.js → chunk-H2H4Y3TV.js} +1 -0
  32. package/dist/chunks/{chunk-MVFDSFV2.js → chunk-HNCXR2VS.js} +1 -1
  33. package/dist/chunks/chunk-JA2HJIYS.js +71 -0
  34. package/dist/chunks/{chunk-PBENCR5L.js → chunk-JBCZIBFC.js} +0 -2
  35. package/dist/chunks/{chunk-5BGP5Z64.js → chunk-JLSJMCDJ.js} +1 -1
  36. package/dist/chunks/{chunk-DT2GGH4D.js → chunk-JMABOQWL.js} +2 -6
  37. package/dist/chunks/{chunk-3S3YVOJ7.js → chunk-JSUCGZ6K.js} +1 -1
  38. package/dist/chunks/{chunk-MLSWIMZO.js → chunk-KBEP7VYK.js} +2 -2
  39. package/dist/chunks/{chunk-DRG4236Y.js → chunk-KC7T5RWG.js} +2 -20
  40. package/dist/chunks/{chunk-TAMCZHLW.js → chunk-N73TI3VG.js} +2 -2
  41. package/dist/chunks/chunk-O4EGE27I.js +10 -0
  42. package/dist/chunks/{chunk-3ZZSX5HN.js → chunk-PTABFYQ7.js} +10 -6
  43. package/dist/chunks/{chunk-WBCOREDZ.js → chunk-QLWI2PZ3.js} +16 -7
  44. package/dist/chunks/chunk-RBCE2OQH.js +32 -0
  45. package/dist/chunks/{chunk-BUBZ32IC.js → chunk-RPRHR56C.js} +4 -8
  46. package/dist/chunks/{chunk-47DYCZOK.js → chunk-RUCH6ZFL.js} +4 -0
  47. package/dist/chunks/{chunk-GNTTI6LA.js → chunk-T2PCSAFY.js} +1 -1
  48. package/dist/chunks/{chunk-XKWD2RY3.js → chunk-URZ3CH2R.js} +3 -10
  49. package/dist/chunks/{chunk-ALDGONEI.js → chunk-VQC7KELB.js} +1 -1
  50. package/dist/chunks/{chunk-FLFBYSJO.js → chunk-VQWHMWLF.js} +5 -1
  51. package/dist/chunks/{chunk-4R6QNG23.js → chunk-WL6ZKUVC.js} +3 -3
  52. package/dist/chunks/{chunk-CRTD2SBP.js → chunk-WZNPX7Q6.js} +6 -2
  53. package/dist/chunks/{chunk-TICYLD5F.js → chunk-XAWLIU54.js} +35 -78
  54. package/dist/chunks/{chunk-DK5P2YOL.js → chunk-XI3ZUTWF.js} +2 -2
  55. package/dist/chunks/{chunk-ZIUKZUPJ.js → chunk-XL472HHD.js} +1 -1
  56. package/dist/chunks/{chunk-BPRXQ2SS.js → chunk-XMW266WS.js} +1 -1
  57. package/dist/chunks/{chunk-WFB5TXRA.js → chunk-ZY2AFL4Y.js} +13 -31
  58. package/dist/cli/decodeText.js +6 -0
  59. package/dist/cli/figure.js +59 -60
  60. package/dist/cli/fold.js +13 -13
  61. package/dist/deletionEdges.js +6 -4
  62. package/dist/el.js +3 -1
  63. package/dist/figure.js +33 -31
  64. package/dist/figureData.js +32 -27
  65. package/dist/figureScene.js +43 -0
  66. package/dist/figureSettings.js +8 -8
  67. package/dist/foldVariants.js +2 -2
  68. package/dist/gbzCut.js +1 -1
  69. package/dist/genes/exonOutline.js +2 -2
  70. package/dist/genes/geneFiles.js +1 -1
  71. package/dist/genes/genePins.js +3 -3
  72. package/dist/gfa/gfaConverter.js +2 -2
  73. package/dist/gfa-core/gfaParser.js +1 -1
  74. package/dist/gfa-core/index.js +1 -1
  75. package/dist/graphLabels.js +4 -4
  76. package/dist/index.js +72 -73
  77. package/dist/labelLayout.js +8 -8
  78. package/dist/layout/forceCache.js +8 -8
  79. package/dist/layout/mergeRuns.js +2 -2
  80. package/dist/layout/orderedLayout.js +1 -3
  81. package/dist/layout/tubeMapLayout.js +6 -6
  82. package/dist/layout/walkRowDraw.js +11 -11
  83. package/dist/layout/walkRowLayout.js +3 -1
  84. package/dist/layout/walkStrip.js +14 -12
  85. package/dist/layoutEngines.js +1 -2
  86. package/dist/layoutModes.js +9 -9
  87. package/dist/nodeWidths.js +1 -3
  88. package/dist/pathColors.js +5 -5
  89. package/dist/pipeline.js +7 -7
  90. package/dist/referenceRampCss.js +9 -9
  91. package/dist/referenceStrip.js +9 -9
  92. package/dist/renderer/Canvas2DRenderer.js +2 -2
  93. package/dist/renderer/GeometryBuilder.js +8 -8
  94. package/dist/renderer/canvas.js +1 -1
  95. package/dist/renderer/svgCanvas.js +1 -1
  96. package/dist/tubeMap/axis.js +5 -5
  97. package/dist/tubeMap/bundle.js +7 -7
  98. package/dist/tubeMap/coarsen.js +6 -6
  99. package/dist/tubeMap/deviations.js +5 -5
  100. package/dist/tubeMap/draw.js +1 -1
  101. package/dist/tubeMap/genes.js +6 -6
  102. package/dist/tubeMap/nodeColors.js +9 -9
  103. package/dist/types/bubbles/bubbleHalos.d.ts +2 -8
  104. package/dist/types/bubbles/bubblesFromGraph.d.ts +1 -0
  105. package/dist/types/bubbles/classifyBubble.d.ts +0 -1
  106. package/dist/types/cli/decodeText.d.ts +1 -0
  107. package/dist/types/cli/figure.d.ts +4 -15
  108. package/dist/types/deletionEdges.d.ts +3 -5
  109. package/dist/types/el.d.ts +1 -0
  110. package/dist/types/figureData.d.ts +7 -26
  111. package/dist/types/figureScene.d.ts +31 -0
  112. package/dist/types/genes/geneFiles.d.ts +2 -1
  113. package/dist/types/index.d.ts +1 -1
  114. package/dist/types/labelLayout.d.ts +1 -5
  115. package/dist/types/layout/deletionRoutes.d.ts +1 -1
  116. package/dist/types/layout/orderedLayout.d.ts +0 -6
  117. package/dist/types/layout/walkRowLayout.d.ts +1 -0
  118. package/dist/types/nodeWidths.d.ts +0 -1
  119. package/dist/types/renderer/Canvas2DRenderer.d.ts +1 -0
  120. package/dist/types/renderer/canvas.d.ts +2 -2
  121. package/dist/types/tubeMap/draw.d.ts +1 -0
  122. package/dist/types/util/geometry.d.ts +3 -3
  123. package/dist/types/version.d.ts +1 -1
  124. package/dist/util/SpatialIndex.js +4 -4
  125. package/dist/util/edgeCurves.js +3 -3
  126. package/dist/util/geometry.js +3 -3
  127. package/dist/util/hitDetection.js +5 -5
  128. package/dist/version.js +1 -1
  129. package/dist/viewport.js +8 -8
  130. package/dist/walkEncoding.js +9 -9
  131. package/dist/walkHighlight.js +10 -10
  132. package/dist/walkKey.js +5 -5
  133. package/package.json +3 -6
  134. package/dist/chunks/bandage-layout-3AR6BCUD.js +0 -4694
  135. package/dist/chunks/chunk-4BYTEJGW.js +0 -44
  136. package/dist/chunks/chunk-7XHHVYG7.js +0 -13
  137. package/dist/chunks/chunk-EHUBJDDB.js +0 -135
  138. package/dist/loadBandage.js +0 -6
  139. package/dist/types/bandage/bandage-layout.d.ts +0 -14
  140. package/dist/types/loadBandage.d.ts +0 -3
@@ -0,0 +1,71 @@
1
+ import {
2
+ bubbleSegmentIds,
3
+ classifyBubble
4
+ } from "./chunk-URZ3CH2R.js";
5
+ import {
6
+ svgPath
7
+ } from "./chunk-7SRWJLSC.js";
8
+ import {
9
+ isBackbone
10
+ } from "./chunk-FYQWPEEY.js";
11
+
12
+ // src/bubbles/bubbleHalos.ts
13
+ var WHOLE_FRACTION = 0.9;
14
+ function coversGraph(members, nodes) {
15
+ return members + 2 >= nodes || members >= WHOLE_FRACTION * nodes;
16
+ }
17
+ var SMALL_VARIANT_BP = 50;
18
+ function bubbleHalos(graph, bubbles, positions, repeats = []) {
19
+ const byName = new Map(graph.nodes.map((n) => [n.name, n]));
20
+ const halos = [];
21
+ for (const bubble of bubbles) {
22
+ const parts = [];
23
+ let minX = Infinity;
24
+ let maxX = -Infinity;
25
+ let minY = Infinity;
26
+ const nodeIds = [];
27
+ const names = bubbleSegmentIds(bubble);
28
+ for (const [i, name] of names.entries()) {
29
+ const node = byName.get(name);
30
+ const line = node && positions[node.id];
31
+ if (!node || !line?.length) {
32
+ continue;
33
+ }
34
+ if (bubble.key ? i === 0 || i === names.length - 1 : isBackbone(node) && (node.stable.start < bubble.start || node.stable.start >= bubble.end)) {
35
+ continue;
36
+ }
37
+ nodeIds.push(node.id);
38
+ parts.push(svgPath(line.length === 1 ? [line[0], line[0]] : line));
39
+ for (const p of line) {
40
+ minX = Math.min(minX, p.x);
41
+ maxX = Math.max(maxX, p.x);
42
+ minY = Math.min(minY, p.y);
43
+ }
44
+ }
45
+ if (nodeIds.length === 0) {
46
+ continue;
47
+ }
48
+ const labelAt = { x: (minX + maxX) / 2, y: minY };
49
+ const whole = bubble.covering === true || coversGraph(nodeIds.length, graph.nodes.length);
50
+ halos.push({
51
+ bubble,
52
+ ...classifyBubble(bubble, repeats),
53
+ path: parts.join(""),
54
+ labelAt,
55
+ members: nodeIds.length,
56
+ nodeIds,
57
+ whole,
58
+ // The drawing's own bubble keeps its name however small. A partial
59
+ // bubble's lengths are a floor, so it may be no small variant.
60
+ tick: !whole && !bubble.partial && bubble.longestAlleleLength < SMALL_VARIANT_BP
61
+ });
62
+ }
63
+ return halos;
64
+ }
65
+
66
+ export {
67
+ WHOLE_FRACTION,
68
+ coversGraph,
69
+ SMALL_VARIANT_BP,
70
+ bubbleHalos
71
+ };
@@ -11,7 +11,6 @@ var NODE_WIDTHS = [
11
11
  description: "Every node the same thickness."
12
12
  }
13
13
  ];
14
- var NODE_WIDTH_VALUES = NODE_WIDTHS.map((w) => w.value);
15
14
  var MIN_FACTOR = 0.3;
16
15
  var MAX_FACTOR = 3;
17
16
  function meanDepth(graph) {
@@ -51,7 +50,6 @@ function nodeInk(graph, nodeById, contigThickness, nodeWidth) {
51
50
 
52
51
  export {
53
52
  NODE_WIDTHS,
54
- NODE_WIDTH_VALUES,
55
53
  meanDepth,
56
54
  depthWidthFactor,
57
55
  nodeWidthPx,
@@ -2,7 +2,7 @@ import {
2
2
  LIFT_BACKDROP_CSS,
3
3
  computeColorSchemeRange,
4
4
  getNodeColor
5
- } from "./chunk-SBMJNRTV.js";
5
+ } from "./chunk-A4PFFC4Q.js";
6
6
  import {
7
7
  abgrToCssRgba
8
8
  } from "./chunk-G2FD7S3C.js";
@@ -1,8 +1,5 @@
1
- import {
2
- loadBandage
3
- } from "./chunk-7XHHVYG7.js";
4
-
5
1
  // src/layoutEngines.ts
2
+ import { fmmmLayout } from "@jbrowse/graph-fmmm-layout";
6
3
  import { stressLayout } from "@jbrowse/graph-stress-layout";
7
4
  var LAYOUT_ENGINES = [
8
5
  {
@@ -26,9 +23,8 @@ var layoutEngine = async (request) => {
26
23
  const result2 = stressLayout(request);
27
24
  return { result: result2, duration: performance.now() - start2 };
28
25
  }
29
- const bandage = await loadBandage();
30
26
  const start = performance.now();
31
- const result = bandage.computeLayout(request.graph, request.options);
27
+ const result = await fmmmLayout(request);
32
28
  return { result, duration: performance.now() - start };
33
29
  };
34
30
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  el
3
- } from "./chunk-NNODIW4U.js";
3
+ } from "./chunk-H2H4Y3TV.js";
4
4
 
5
5
  // src/genes/exonOutline.ts
6
6
  var EXON_COLOR = "#daa520";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  baseEdgeCurves
3
- } from "./chunk-WFB5TXRA.js";
3
+ } from "./chunk-ZY2AFL4Y.js";
4
4
  import {
5
5
  curveBounds,
6
6
  pathRibbonOffsets
7
- } from "./chunk-2CVHXFK7.js";
7
+ } from "./chunk-7SRWJLSC.js";
8
8
 
9
9
  // src/util/SpatialIndex.ts
10
10
  var FALLBACK_CELL_SIZE = 50;
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  nodesUnderBox
3
- } from "./chunk-FP4H7SFT.js";
3
+ } from "./chunk-4HWM3FNK.js";
4
4
  import {
5
5
  nodeLabelBudget,
6
6
  placeSizeLabels,
7
7
  rowLabelBox,
8
8
  sizeLabelCandidates
9
- } from "./chunk-3ZZSX5HN.js";
9
+ } from "./chunk-PTABFYQ7.js";
10
10
  import {
11
11
  LABEL_CHAR_PX,
12
12
  LABEL_PAD,
@@ -18,7 +18,6 @@ import {
18
18
  // src/labelLayout.ts
19
19
  var HALO_FACTOR = 3.4;
20
20
  var LEGEND_INSET_PX = 6;
21
- var ROUTE_STACK = 8;
22
21
  var GENE_PIN_DROP_PX = 18;
23
22
  var GENE_STACK = 2;
24
23
  var BUBBLE_STACK = 3;
@@ -147,22 +146,6 @@ function layoutLabels(m) {
147
146
  version: m.positionsVersion
148
147
  }) : { deletions: [], nodes: [] };
149
148
  const deletionLabels = placeSizeLabels(sizeCandidates.deletions, take);
150
- const routes = placeLabels(
151
- byBubble.flatMap(
152
- (h) => h.routes.map((route) => {
153
- const { x, y } = screen(route.at);
154
- return {
155
- item: { halo: h, route },
156
- x,
157
- y: y + 4,
158
- text: route.text,
159
- stack: ROUTE_STACK
160
- };
161
- })
162
- ),
163
- frame,
164
- take
165
- );
166
149
  const nodeLabels = placeSizeLabels(
167
150
  sizeCandidates.nodes,
168
151
  take,
@@ -172,7 +155,6 @@ function layoutLabels(m) {
172
155
  bubbles,
173
156
  ticks,
174
157
  genes,
175
- routes,
176
158
  sizes: [...deletionLabels, ...nodeLabels]
177
159
  };
178
160
  }
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ownStrand,
3
3
  strandSides
4
- } from "./chunk-2CVHXFK7.js";
4
+ } from "./chunk-7SRWJLSC.js";
5
5
  import {
6
6
  isBackbone
7
7
  } from "./chunk-FYQWPEEY.js";
@@ -122,7 +122,7 @@ function splitRuns(positions, runs, weightOf) {
122
122
  const arc = arcPrefix(line);
123
123
  const total = arc.at(-1);
124
124
  const weights = members.map((m) => Math.max(weightOf(m), 0));
125
- const weightSum = weights.reduce((a, b) => a + b, 0) || members.length;
125
+ const weightSum = weights.reduce((a, b) => a + b, 0);
126
126
  let from = 0;
127
127
  members.forEach((member, k) => {
128
128
  const share = weightSum ? weights[k] / weightSum * total : total / members.length;
@@ -0,0 +1,10 @@
1
+ // src/cli/decodeText.ts
2
+ import { gunzipSync } from "node:zlib";
3
+ function decodeText(bytes) {
4
+ const gzipped = bytes[0] === 31 && bytes[1] === 139;
5
+ return new TextDecoder().decode(gzipped ? gunzipSync(bytes) : bytes);
6
+ }
7
+
8
+ export {
9
+ decodeText
10
+ };
@@ -3,14 +3,14 @@ import {
3
3
  } from "./chunk-AB35TJST.js";
4
4
  import {
5
5
  polylineMidpoint
6
- } from "./chunk-TAMCZHLW.js";
6
+ } from "./chunk-N73TI3VG.js";
7
7
  import {
8
8
  deletionArcCurves
9
- } from "./chunk-N6FECAXP.js";
9
+ } from "./chunk-24WITXQY.js";
10
10
  import {
11
11
  curveBounds,
12
12
  curveMidpoint
13
- } from "./chunk-2CVHXFK7.js";
13
+ } from "./chunk-7SRWJLSC.js";
14
14
 
15
15
  // src/graphLabels.ts
16
16
  var MIN_DELETION_LABEL_PX = 26;
@@ -50,10 +50,11 @@ function groupDigits(n) {
50
50
  return String(n).replace(/\B(?=(\d{3})+$)/g, ",");
51
51
  }
52
52
  function formatBp(bp) {
53
- if (bp >= 1e6) {
54
- return `${+(bp / 1e6).toFixed(1)} Mb`;
53
+ const [value, unit] = bp >= 1e6 ? [bp / 1e6, "Mb"] : bp >= 1e3 ? [bp / 1e3, "kb"] : [];
54
+ if (value === void 0) {
55
+ return `${bp} bp`;
55
56
  }
56
- return bp >= 1e3 ? `${+(bp / 1e3).toFixed(1)} kb` : `${bp} bp`;
57
+ return `${+value.toFixed(value >= 100 ? 0 : 1)} ${unit}`;
57
58
  }
58
59
  function deletionText(bp) {
59
60
  return `${formatBp(bp)} del`;
@@ -181,6 +182,9 @@ function sizeLabelCandidates({
181
182
  const box = boxAt(halfW, 0, 0);
182
183
  const arcX = apex.x * scaleX + translateX;
183
184
  const arcY = apex.y * scaleY + translateY;
185
+ if (arcX < 0 || arcX > width || arcY < 0 || arcY > height) {
186
+ continue;
187
+ }
184
188
  const fits = extent >= halfW * 2 / MAX_LABEL_OVERHANG;
185
189
  const bow = arcOutward(curves, apex, scaleX, scaleY);
186
190
  const offset = boxSupport(box, bow) + LEADER_GAP_PX;
@@ -1,6 +1,7 @@
1
1
  import {
2
+ getDpr,
2
3
  syncCanvasSize
3
- } from "./chunk-4BYTEJGW.js";
4
+ } from "./chunk-RBCE2OQH.js";
4
5
  import {
5
6
  abgrToCssRgba,
6
7
  brightenAbgr,
@@ -15,6 +16,7 @@ var Canvas2DRenderer = class {
15
16
  this.nodeHighlights = /* @__PURE__ */ new Map();
16
17
  this.highlightedEdge = null;
17
18
  this.highlightFactor = 1;
19
+ this.fit = 1;
18
20
  const ctx = canvas.getContext("2d");
19
21
  if (!ctx) {
20
22
  throw new Error("Canvas 2D context not available");
@@ -26,12 +28,11 @@ var Canvas2DRenderer = class {
26
28
  }
27
29
  releaseOffscreenTargets() {
28
30
  }
29
- // Not a local `width * devicePixelRatio`: syncCanvasSize clamps the backing
30
- // store at MAX_CANVAS_DIM_PX, reads the ratio through `getDpr()` so this
31
- // agrees with the transform the model builds, and writes the css size
32
- // independently of the backing size.
31
+ // A host builds its transform at `getDpr()`. Where syncCanvasSize had to
32
+ // shrink the ratio to stay under MAX_CANVAS_DIM_PX, `fit` shrinks the
33
+ // transform to match, so the picture still fills the css box.
33
34
  resize(width, height) {
34
- syncCanvasSize(this.canvas, width, height);
35
+ this.fit = syncCanvasSize(this.canvas, width, height) / getDpr();
35
36
  }
36
37
  uploadGeometry(batch) {
37
38
  this.batch = batch;
@@ -45,7 +46,15 @@ var Canvas2DRenderer = class {
45
46
  this.highlightFactor = factor;
46
47
  }
47
48
  updateTransform(transform) {
48
- this.transform = transform;
49
+ const k = this.fit;
50
+ this.transform = k === 1 ? transform : {
51
+ ...transform,
52
+ scaleX: transform.scaleX * k,
53
+ scaleY: transform.scaleY * k,
54
+ translateX: transform.translateX * k,
55
+ translateY: transform.translateY * k,
56
+ dpr: transform.dpr * k
57
+ };
49
58
  }
50
59
  render(clearColor) {
51
60
  const t = this.transform;
@@ -0,0 +1,32 @@
1
+ // src/renderer/canvas.ts
2
+ var MAX_DPR = 2;
3
+ var MAX_CANVAS_DIM_PX = 8192;
4
+ function getDpr(width = 0, height = 0) {
5
+ const dpr = typeof devicePixelRatio === "undefined" ? 1 : Math.min(devicePixelRatio, MAX_DPR);
6
+ return Math.min(dpr, MAX_CANVAS_DIM_PX / Math.max(width, height, 1));
7
+ }
8
+ function syncCanvasSize(canvas, width, height) {
9
+ const dpr = getDpr(width, height);
10
+ const pw = Math.round(width * dpr);
11
+ const ph = Math.round(height * dpr);
12
+ if (canvas.width !== pw || canvas.height !== ph) {
13
+ canvas.width = pw;
14
+ canvas.height = ph;
15
+ }
16
+ const cssWidth = `${width}px`;
17
+ const cssHeight = `${height}px`;
18
+ if (canvas.style.width !== cssWidth) {
19
+ canvas.style.width = cssWidth;
20
+ }
21
+ if (canvas.style.height !== cssHeight) {
22
+ canvas.style.height = cssHeight;
23
+ }
24
+ return dpr;
25
+ }
26
+
27
+ export {
28
+ MAX_DPR,
29
+ MAX_CANVAS_DIM_PX,
30
+ getDpr,
31
+ syncCanvasSize
32
+ };
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  polylineSlice
3
- } from "./chunk-TAMCZHLW.js";
3
+ } from "./chunk-N73TI3VG.js";
4
4
  import {
5
5
  svgPath
6
- } from "./chunk-2CVHXFK7.js";
6
+ } from "./chunk-7SRWJLSC.js";
7
7
  import {
8
8
  firstNodeAtOrAfter,
9
9
  isAnchored
@@ -74,12 +74,8 @@ function genePins(graph, genes, positions) {
74
74
  const distance = Math.abs(pinBp - mid);
75
75
  if (distance < atDistance) {
76
76
  atDistance = distance;
77
- const [p] = polylineSlice(
78
- line,
79
- (pinBp - nodeStart) / node.length,
80
- (pinBp - nodeStart) / node.length
81
- );
82
- at = p;
77
+ const along = node.length ? (pinBp - nodeStart) / node.length : 0;
78
+ at = polylineSlice(line, along, along)[0];
83
79
  }
84
80
  }
85
81
  if (at) {
@@ -90,6 +90,10 @@ function svgCanvas(width, height) {
90
90
  out.push(`<path d="${d}" ${paintAttrs("fill", ctx.fillStyle)}/>`);
91
91
  }
92
92
  },
93
+ // the renderer only ever clears the whole canvas
94
+ clearRect() {
95
+ out.length = 0;
96
+ },
93
97
  fillRect(x, y, w, h) {
94
98
  out.push(
95
99
  `<rect x="${n(x)}" y="${n(y)}" width="${n(w)}" height="${n(h)}" ${paintAttrs("fill", ctx.fillStyle)}/>`
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  computeColorSchemeRange,
3
3
  getNodeColor
4
- } from "./chunk-SBMJNRTV.js";
4
+ } from "./chunk-A4PFFC4Q.js";
5
5
  import {
6
6
  abgrToCssRgba
7
7
  } from "./chunk-G2FD7S3C.js";
@@ -1,6 +1,9 @@
1
1
  import {
2
2
  SATURATED_PATH_COUNT
3
3
  } from "./chunk-FX4RDMDN.js";
4
+ import {
5
+ formatBp
6
+ } from "./chunk-PTABFYQ7.js";
4
7
 
5
8
  // src/bubbles/classifyBubble.ts
6
9
  var REPEAT_GROWTH = 10;
@@ -12,15 +15,6 @@ function coveringRepeat(b, repeats) {
12
15
  );
13
16
  }
14
17
  var SUPERBUBBLE_SEGMENTS = 40;
15
- function formatBp(bp) {
16
- if (bp >= 1e4) {
17
- return `${(bp / 1e3).toFixed(0)} kb`;
18
- }
19
- if (bp >= 1e3) {
20
- return `${(bp / 1e3).toFixed(1)} kb`;
21
- }
22
- return `${bp} bp`;
23
- }
24
18
  function formatBpRange(shortest, longest) {
25
19
  const low = formatBp(shortest);
26
20
  const high = formatBp(longest);
@@ -119,7 +113,6 @@ function bubbleSegmentIds(b) {
119
113
  }
120
114
 
121
115
  export {
122
- formatBp,
123
116
  classifyBubble,
124
117
  BUBBLE_KIND_COLORS,
125
118
  BUBBLE_KIND_NAMES,
@@ -2,7 +2,7 @@ import {
2
2
  REFERENCE_RAMP_ALT_COLOR,
3
3
  REFERENCE_RAMP_MAX_HUE,
4
4
  hslToRgb
5
- } from "./chunk-SBMJNRTV.js";
5
+ } from "./chunk-A4PFFC4Q.js";
6
6
  import {
7
7
  packAbgr
8
8
  } from "./chunk-G2FD7S3C.js";
@@ -25,7 +25,11 @@ function parseRegion(text) {
25
25
  );
26
26
  }
27
27
  const n = (s) => Number(s.replaceAll(",", ""));
28
- return { refName: m[1], start: n(m[2]), end: n(m[3]) };
28
+ const region = { refName: m[1], start: n(m[2]), end: n(m[3]) };
29
+ if (region.end <= region.start) {
30
+ throw new Error(`"${text}" ends at or before it starts`);
31
+ }
32
+ return region;
29
33
  }
30
34
  async function openGbz(db, index) {
31
35
  const base = await GBZBase.open({ source: db, haplotypeIndex: index });
@@ -6,16 +6,16 @@ import {
6
6
  hasTubeMapPaths,
7
7
  tubeMapLayout,
8
8
  tubeMapReferenceLayout
9
- } from "./chunk-DK5P2YOL.js";
9
+ } from "./chunk-XI3ZUTWF.js";
10
10
  import {
11
11
  anchoredLayout
12
12
  } from "./chunk-L3O7AWLI.js";
13
13
  import {
14
14
  orderedLayout
15
- } from "./chunk-HNQPEMYE.js";
15
+ } from "./chunk-CM6LO6OC.js";
16
16
  import {
17
17
  walkRowLayout
18
- } from "./chunk-C7MZJSPT.js";
18
+ } from "./chunk-54POIBVC.js";
19
19
  import {
20
20
  isBackbone,
21
21
  isOffReference
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  engineKey,
3
3
  forceLayout
4
- } from "./chunk-XTCNEKO5.js";
4
+ } from "./chunk-2QLLQJ72.js";
5
5
 
6
6
  // src/layout/forceCache.ts
7
7
  var DEFAULT_SIZE = 4;
@@ -33,7 +33,11 @@ function createForceLayoutCache(size = DEFAULT_SIZE) {
33
33
  return { ...r, duration: r.duration || performance.now() - started };
34
34
  })
35
35
  };
36
- entry.run.catch(() => cache.delete(key));
36
+ entry.run.catch(() => {
37
+ if (cache.get(key) === entry) {
38
+ cache.delete(key);
39
+ }
40
+ });
37
41
  cache.set(key, entry);
38
42
  return entry.run;
39
43
  },
@@ -1,32 +1,24 @@
1
1
  import {
2
2
  layoutEngine
3
- } from "./chunk-DT2GGH4D.js";
3
+ } from "./chunk-JMABOQWL.js";
4
4
  import {
5
5
  layoutModeByValue
6
- } from "./chunk-4R6QNG23.js";
6
+ } from "./chunk-WL6ZKUVC.js";
7
7
  import {
8
8
  parseRegion
9
- } from "./chunk-FLFBYSJO.js";
10
- import {
11
- layoutGeometryInputs
12
- } from "./chunk-LOCJN3BZ.js";
9
+ } from "./chunk-VQWHMWLF.js";
13
10
  import {
14
11
  version
15
- } from "./chunk-EYK7DJZV.js";
12
+ } from "./chunk-2CHS5LMM.js";
16
13
  import {
17
14
  axisScaleOf
18
- } from "./chunk-L5WF4ZPS.js";
19
- import {
20
- facetLifts,
21
- walkLift
22
- } from "./chunk-R456ME6J.js";
15
+ } from "./chunk-BS4UZ44I.js";
23
16
  import {
24
17
  walkKey
25
- } from "./chunk-2LQ5BWT7.js";
18
+ } from "./chunk-4ETPXDO6.js";
26
19
  import {
27
- buildGeometry,
28
- computeReferenceRamp
29
- } from "./chunk-SBMJNRTV.js";
20
+ figureScene
21
+ } from "./chunk-D5D36ZF6.js";
30
22
  import {
31
23
  abgrAlpha,
32
24
  abgrBlue,
@@ -34,32 +26,16 @@ import {
34
26
  abgrRed
35
27
  } from "./chunk-G2FD7S3C.js";
36
28
  import {
37
- pathLegend,
38
- walkLabelsOf
39
- } from "./chunk-TWRLC3KK.js";
40
- import {
41
- drawingBounds,
42
29
  engineSettingsOf,
43
30
  fitTransform,
44
31
  forceLayout,
45
32
  loadGraph
46
- } from "./chunk-XTCNEKO5.js";
47
- import {
48
- referenceLabel
49
- } from "./chunk-RAKOB6IV.js";
50
- import {
51
- resolveColorScheme
52
- } from "./chunk-JKJWSY4V.js";
53
- import {
54
- deletionDrawing,
55
- deletionEdges
56
- } from "./chunk-N6FECAXP.js";
33
+ } from "./chunk-2QLLQJ72.js";
57
34
  import {
58
35
  isBackbone
59
36
  } from "./chunk-FYQWPEEY.js";
60
37
  import {
61
- facetCells,
62
- facetSettingOf
38
+ facetCells
63
39
  } from "./chunk-XV4P5KEQ.js";
64
40
 
65
41
  // src/figureData.ts
@@ -85,39 +61,24 @@ function figureData(graph, layout, o = {}) {
85
61
  }
86
62
  const width = o.width ?? 1200;
87
63
  const height = o.height ?? 800;
88
- const region = o.region;
89
64
  const pixelRows = layout.pixelRows ?? false;
90
- const bounds = drawingBounds(layout, {
91
- region: o.fitToDrawing ? void 0 : region
92
- });
65
+ const {
66
+ bounds,
67
+ colorScheme,
68
+ drawnDeletions,
69
+ lift,
70
+ facet,
71
+ panels: facetted,
72
+ labelOf,
73
+ referenceName,
74
+ reference,
75
+ geometry
76
+ } = figureScene(graph, layout, o);
93
77
  const fit = fitTransform(bounds, width, height, pixelRows);
94
78
  if (!fit) {
95
79
  throw new Error("the layout drew nothing to fit");
96
80
  }
97
81
  const axis = axisScaleOf(fit.scale, pixelRows);
98
- const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
99
- const colorScheme = resolveColorScheme(o.colorScheme ?? "auto", graph);
100
- const rampDomain = o.colorDomain ?? region;
101
- const referenceRamp = colorScheme === "reference-position" ? computeReferenceRamp(graph, rampDomain) : void 0;
102
- const drawnDeletions = deletionDrawing(
103
- graph,
104
- deletionEdges(graph),
105
- o.showDeletionEdges
106
- );
107
- const layers = o.walks ?? [];
108
- const walkRamp = layers.some((l) => l.color?.field === "reference") ? referenceRamp ?? computeReferenceRamp(graph, rampDomain) : void 0;
109
- const lift = layers.length > 0 ? walkLift(graph, layers, walkRamp) : void 0;
110
- const facet = facetSettingOf(o.facet);
111
- const facetted = facet.field && lift && lift.walks.length > 1 ? facetLifts(graph, lift, layers, walkRamp) : void 0;
112
- const labels = walkLabelsOf(
113
- graph.paths?.length ? pathLegend(graph.paths) : []
114
- );
115
- const labelOf = (name) => labels.get(name) ?? name;
116
- const referenceName = o.referenceName ?? referenceLabel(graph, region);
117
- const reference = lift?.referenceDomain && {
118
- ...lift.referenceDomain,
119
- name: referenceName
120
- };
121
82
  const panels = [];
122
83
  if (facetted) {
123
84
  const place = facetCells(
@@ -178,20 +139,7 @@ function figureData(graph, layout, o = {}) {
178
139
  "alpha"
179
140
  ]);
180
141
  for (const { highlight, name: panel } of panels) {
181
- const batch = buildGeometry({
182
- ...layoutGeometryInputs(layout, drawnDeletions),
183
- graph,
184
- nodeById,
185
- colorScheme,
186
- contigThickness: o.contigThickness ?? 6,
187
- connectorThickness: o.connectorThickness ?? 2,
188
- drawPaths: false,
189
- nodeWidth: o.nodeWidth ?? "depth",
190
- linearLayout: o.linearLayout,
191
- highlight,
192
- axis,
193
- referenceRamp
194
- });
142
+ const batch = geometry(highlight, axis);
195
143
  for (const [node, run] of batch.nodeStrokeRuns) {
196
144
  for (let i = run.start; i < run.start + run.count; i++) {
197
145
  const s = batch.nodeStrokes[i];
@@ -275,18 +223,27 @@ function figureData(graph, layout, o = {}) {
275
223
  }
276
224
  };
277
225
  }
278
- async function gfaFigureData(text, name, spec = {}) {
279
- const region = spec.region ? parseRegion(spec.region) : void 0;
226
+ async function laidOutGraph(text, name, region, spec) {
280
227
  const graph = loadGraph(text, name, { referencePath: spec.referencePath });
281
228
  const layout = layoutModeByValue(spec.layout ?? "force").run(graph, region) ?? (await forceLayout(graph, engineSettingsOf(spec), layoutEngine)).result;
229
+ return { graph, layout };
230
+ }
231
+ function walkLayers(walks) {
232
+ return walks?.map((w) => typeof w === "string" ? { walk: w } : w);
233
+ }
234
+ async function gfaFigureData(text, name, spec = {}) {
235
+ const region = spec.region ? parseRegion(spec.region) : void 0;
236
+ const { graph, layout } = await laidOutGraph(text, name, region, spec);
282
237
  return figureData(graph, layout, {
283
238
  ...spec,
284
239
  region,
285
- walks: spec.walks?.map((w) => typeof w === "string" ? { walk: w } : w)
240
+ walks: walkLayers(spec.walks)
286
241
  });
287
242
  }
288
243
 
289
244
  export {
290
245
  figureData,
246
+ laidOutGraph,
247
+ walkLayers,
291
248
  gfaFigureData
292
249
  };