@jbrowse/plugin-linear-comparative-view 3.6.4 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/LGVSyntenyDisplay/configSchemaF.js +1 -1
  2. package/dist/LGVSyntenyDisplay/model.d.ts +17 -2
  3. package/dist/LinearComparativeView/components/ColorBySelector.d.ts +5 -0
  4. package/dist/LinearComparativeView/components/ColorBySelector.js +54 -0
  5. package/dist/LinearComparativeView/components/Header.js +7 -2
  6. package/dist/LinearComparativeView/components/MinLengthSlider.d.ts +5 -0
  7. package/dist/LinearComparativeView/components/MinLengthSlider.js +47 -0
  8. package/dist/LinearComparativeView/components/OpacitySlider.d.ts +5 -0
  9. package/dist/LinearComparativeView/components/OpacitySlider.js +46 -0
  10. package/dist/LinearComparativeView/components/RubberbandSpan.js +2 -2
  11. package/dist/LinearComparativeView/components/SliderTooltip.d.ts +2 -0
  12. package/dist/LinearComparativeView/components/SliderTooltip.js +9 -0
  13. package/dist/LinearComparativeView/components/useRangeSelect.js +10 -14
  14. package/dist/LinearComparativeView/model.d.ts +7 -4
  15. package/dist/LinearComparativeView/model.js +4 -0
  16. package/dist/LinearSyntenyDisplay/afterAttach.js +5 -3
  17. package/dist/LinearSyntenyDisplay/components/LinearSyntenyRendering.js +10 -5
  18. package/dist/LinearSyntenyDisplay/components/util.d.ts +2 -1
  19. package/dist/LinearSyntenyDisplay/components/util.js +43 -2
  20. package/dist/LinearSyntenyDisplay/drawSynteny.d.ts +3 -2
  21. package/dist/LinearSyntenyDisplay/drawSynteny.js +284 -45
  22. package/dist/LinearSyntenyDisplay/model.d.ts +6 -0
  23. package/dist/LinearSyntenyDisplay/model.js +12 -0
  24. package/dist/LinearSyntenyView/components/DiagonalizationProgressDialog.d.ts +6 -0
  25. package/dist/LinearSyntenyView/components/DiagonalizationProgressDialog.js +87 -0
  26. package/dist/LinearSyntenyView/model.d.ts +46 -15
  27. package/dist/LinearSyntenyView/model.js +70 -18
  28. package/dist/LinearSyntenyView/util/diagonalize.d.ts +27 -0
  29. package/dist/LinearSyntenyView/util/diagonalize.js +91 -0
  30. package/esm/LGVSyntenyDisplay/configSchemaF.js +1 -1
  31. package/esm/LGVSyntenyDisplay/model.d.ts +17 -2
  32. package/esm/LinearComparativeView/components/ColorBySelector.d.ts +5 -0
  33. package/esm/LinearComparativeView/components/ColorBySelector.js +49 -0
  34. package/esm/LinearComparativeView/components/Header.js +8 -3
  35. package/esm/LinearComparativeView/components/MinLengthSlider.d.ts +5 -0
  36. package/esm/LinearComparativeView/components/MinLengthSlider.js +42 -0
  37. package/esm/LinearComparativeView/components/OpacitySlider.d.ts +5 -0
  38. package/esm/LinearComparativeView/components/OpacitySlider.js +41 -0
  39. package/esm/LinearComparativeView/components/RubberbandSpan.js +2 -2
  40. package/esm/LinearComparativeView/components/SliderTooltip.d.ts +2 -0
  41. package/esm/LinearComparativeView/components/SliderTooltip.js +6 -0
  42. package/esm/LinearComparativeView/components/useRangeSelect.js +11 -15
  43. package/esm/LinearComparativeView/model.d.ts +7 -4
  44. package/esm/LinearComparativeView/model.js +4 -0
  45. package/esm/LinearSyntenyDisplay/afterAttach.js +6 -4
  46. package/esm/LinearSyntenyDisplay/components/LinearSyntenyRendering.js +10 -5
  47. package/esm/LinearSyntenyDisplay/components/util.d.ts +2 -1
  48. package/esm/LinearSyntenyDisplay/components/util.js +43 -3
  49. package/esm/LinearSyntenyDisplay/drawSynteny.d.ts +3 -2
  50. package/esm/LinearSyntenyDisplay/drawSynteny.js +283 -45
  51. package/esm/LinearSyntenyDisplay/model.d.ts +6 -0
  52. package/esm/LinearSyntenyDisplay/model.js +12 -0
  53. package/esm/LinearSyntenyView/components/DiagonalizationProgressDialog.d.ts +6 -0
  54. package/esm/LinearSyntenyView/components/DiagonalizationProgressDialog.js +85 -0
  55. package/esm/LinearSyntenyView/model.d.ts +46 -15
  56. package/esm/LinearSyntenyView/model.js +70 -18
  57. package/esm/LinearSyntenyView/util/diagonalize.d.ts +27 -0
  58. package/esm/LinearSyntenyView/util/diagonalize.js +88 -0
  59. package/package.json +5 -5
@@ -2,9 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MAX_COLOR_RANGE = void 0;
4
4
  exports.getId = getId;
5
+ exports.drawCigarClickMap = drawCigarClickMap;
5
6
  exports.drawRef = drawRef;
6
- exports.drawMouseoverSynteny = drawMouseoverSynteny;
7
+ exports.drawMouseoverClickMap = drawMouseoverClickMap;
8
+ const colors_1 = require("@jbrowse/core/ui/colors");
7
9
  const util_1 = require("@jbrowse/core/util");
10
+ const colord_1 = require("@jbrowse/core/util/colord");
8
11
  const util_2 = require("./components/util");
9
12
  exports.MAX_COLOR_RANGE = 255 * 255 * 255;
10
13
  function makeColor(idx) {
@@ -13,7 +16,20 @@ function makeColor(idx) {
13
16
  const b = idx % 255;
14
17
  return `rgb(${r},${g},${b})`;
15
18
  }
16
- const colorMap = {
19
+ function hashString(str) {
20
+ let hash = 0;
21
+ for (let i = 0; i < str.length; i++) {
22
+ const char = str.charCodeAt(i);
23
+ hash = (hash << 5) - hash + char;
24
+ hash = hash & hash;
25
+ }
26
+ return Math.abs(hash);
27
+ }
28
+ function getQueryColor(queryName) {
29
+ const hash = hashString(queryName);
30
+ return colors_1.category10[hash % colors_1.category10.length];
31
+ }
32
+ const defaultCigarColors = {
17
33
  I: '#ff03',
18
34
  N: '#0a03',
19
35
  D: '#00f3',
@@ -21,13 +37,39 @@ const colorMap = {
21
37
  M: '#f003',
22
38
  '=': '#f003',
23
39
  };
40
+ const strandCigarColors = {
41
+ I: '#ff03',
42
+ N: '#a020f0',
43
+ D: '#a020f0',
44
+ X: 'brown',
45
+ M: '#f003',
46
+ '=': '#f003',
47
+ };
48
+ const colorSchemes = {
49
+ default: {
50
+ cigarColors: defaultCigarColors,
51
+ },
52
+ strand: {
53
+ posColor: 'red',
54
+ negColor: 'blue',
55
+ cigarColors: strandCigarColors,
56
+ },
57
+ query: {
58
+ cigarColors: defaultCigarColors,
59
+ },
60
+ };
61
+ function applyAlpha(color, alpha) {
62
+ if (alpha === 1) {
63
+ return color;
64
+ }
65
+ return (0, colord_1.colord)(color).alpha(alpha).toHex();
66
+ }
24
67
  const lineLimit = 3;
25
68
  const oobLimit = 1600;
26
69
  function getId(r, g, b, unitMultiplier) {
27
70
  return Math.floor((r * 255 * 255 + g * 255 + b - 1) / unitMultiplier);
28
71
  }
29
- function drawRef(model, ctx1, ctx3) {
30
- var _a;
72
+ function drawCigarClickMap(model, cigarClickMapCanvas) {
31
73
  const view = (0, util_1.getContainingView)(model);
32
74
  const drawCurves = view.drawCurves;
33
75
  const drawCIGAR = view.drawCIGAR;
@@ -35,55 +77,226 @@ function drawRef(model, ctx1, ctx3) {
35
77
  const { level, height, featPositions } = model;
36
78
  const width = view.width;
37
79
  const bpPerPxs = view.views.map(v => v.bpPerPx);
38
- if (ctx3) {
39
- ctx3.imageSmoothingEnabled = false;
40
- }
41
- ctx1.beginPath();
80
+ cigarClickMapCanvas.imageSmoothingEnabled = false;
81
+ cigarClickMapCanvas.clearRect(0, 0, width, height);
42
82
  const offsets = view.views.map(v => v.offsetPx);
43
- const unitMultiplier = Math.floor(exports.MAX_COLOR_RANGE / featPositions.length);
44
- ctx1.fillStyle = colorMap.M;
45
- ctx1.strokeStyle = colorMap.M;
46
- for (const { p11, p12, p21, p22 } of featPositions) {
83
+ const bpPerPxInv0 = 1 / bpPerPxs[level];
84
+ const bpPerPxInv1 = 1 / bpPerPxs[level + 1];
85
+ for (const { p11, p12, p21, p22, f, cigar } of featPositions) {
47
86
  const x11 = p11.offsetPx - offsets[level];
48
87
  const x12 = p12.offsetPx - offsets[level];
49
88
  const x21 = p21.offsetPx - offsets[level + 1];
50
89
  const x22 = p22.offsetPx - offsets[level + 1];
51
90
  const l1 = Math.abs(x12 - x11);
52
91
  const l2 = Math.abs(x22 - x21);
92
+ const minX = Math.min(x21, x22);
93
+ const maxX = Math.max(x21, x22);
53
94
  const y1 = 0;
54
95
  const y2 = height;
55
96
  const mid = (y2 - y1) / 2;
97
+ if (!(l1 <= lineLimit && l2 <= lineLimit) &&
98
+ (0, util_1.doesIntersect2)(minX, maxX, -oobLimit, view.width + oobLimit)) {
99
+ const s1 = f.get('strand');
100
+ const k1 = s1 === -1 ? x12 : x11;
101
+ const k2 = s1 === -1 ? x11 : x12;
102
+ const rev1 = k1 < k2 ? 1 : -1;
103
+ const rev2 = (x21 < x22 ? 1 : -1) * s1;
104
+ let cx1 = k1;
105
+ let cx2 = s1 === -1 ? x22 : x21;
106
+ if (cigar.length && drawCIGAR) {
107
+ let continuingFlag = false;
108
+ let px1 = 0;
109
+ let px2 = 0;
110
+ const unitMultiplier2 = Math.floor(exports.MAX_COLOR_RANGE / cigar.length);
111
+ for (let j = 0; j < cigar.length; j += 2) {
112
+ const len = +cigar[j];
113
+ const op = cigar[j + 1];
114
+ if (!continuingFlag) {
115
+ px1 = cx1;
116
+ px2 = cx2;
117
+ }
118
+ const d1 = len * bpPerPxInv0;
119
+ const d2 = len * bpPerPxInv1;
120
+ if (op === 'M' || op === '=' || op === 'X') {
121
+ cx1 += d1 * rev1;
122
+ cx2 += d2 * rev2;
123
+ }
124
+ else if (op === 'D' || op === 'N') {
125
+ cx1 += d1 * rev1;
126
+ }
127
+ else if (op === 'I') {
128
+ cx2 += d2 * rev2;
129
+ }
130
+ if (!(Math.max(px1, px2, cx1, cx2) < 0 ||
131
+ Math.min(px1, px2, cx1, cx2) > width)) {
132
+ const isNotLast = j < cigar.length - 2;
133
+ if (Math.abs(cx1 - px1) <= 1 &&
134
+ Math.abs(cx2 - px2) <= 1 &&
135
+ isNotLast) {
136
+ continuingFlag = true;
137
+ }
138
+ else {
139
+ continuingFlag = false;
140
+ const shouldDraw = !drawCIGARMatchesOnly ||
141
+ ((op === 'M' || op === '=' || op === 'X') &&
142
+ Math.abs(cx1 - px1) > 1 &&
143
+ Math.abs(cx2 - px2) > 1);
144
+ if (shouldDraw) {
145
+ const idx = j * unitMultiplier2 + 1;
146
+ cigarClickMapCanvas.fillStyle = makeColor(idx);
147
+ (0, util_2.draw)(cigarClickMapCanvas, px1, cx1, y1, cx2, px2, y2, mid, drawCurves);
148
+ cigarClickMapCanvas.fill();
149
+ }
150
+ }
151
+ }
152
+ }
153
+ }
154
+ }
155
+ }
156
+ }
157
+ function drawRef(model, mainCanvas) {
158
+ var _a;
159
+ const view = (0, util_1.getContainingView)(model);
160
+ const drawCurves = view.drawCurves;
161
+ const drawCIGAR = view.drawCIGAR;
162
+ const drawCIGARMatchesOnly = view.drawCIGARMatchesOnly;
163
+ const drawLocationMarkersEnabled = view.drawLocationMarkers;
164
+ const { level, height, featPositions, alpha, minAlignmentLength, colorBy } = model;
165
+ const width = view.width;
166
+ const bpPerPxs = view.views.map(v => v.bpPerPx);
167
+ const queryTotalLengths = new Map();
168
+ if (minAlignmentLength > 0) {
169
+ for (const { f } of featPositions) {
170
+ const queryName = f.get('name') || f.get('id') || f.id();
171
+ const alignmentLength = Math.abs(f.get('end') - f.get('start'));
172
+ const currentTotal = queryTotalLengths.get(queryName) || 0;
173
+ queryTotalLengths.set(queryName, currentTotal + alignmentLength);
174
+ }
175
+ }
176
+ const schemeConfig = colorSchemes[colorBy] || colorSchemes.default;
177
+ const activeColorMap = schemeConfig.cigarColors;
178
+ const posColor = colorBy === 'strand' ? colorSchemes.strand.posColor : 'red';
179
+ const negColor = colorBy === 'strand' ? colorSchemes.strand.negColor : 'blue';
180
+ const colorMapWithAlpha = {
181
+ I: applyAlpha(activeColorMap.I, alpha),
182
+ N: applyAlpha(activeColorMap.N, alpha),
183
+ D: applyAlpha(activeColorMap.D, alpha),
184
+ X: applyAlpha(activeColorMap.X, alpha),
185
+ M: applyAlpha(activeColorMap.M, alpha),
186
+ '=': applyAlpha(activeColorMap['='], alpha),
187
+ };
188
+ const posColorWithAlpha = applyAlpha(posColor, alpha);
189
+ const negColorWithAlpha = applyAlpha(negColor, alpha);
190
+ const queryColorCache = new Map();
191
+ const getQueryColorWithAlpha = (queryName) => {
192
+ if (!queryColorCache.has(queryName)) {
193
+ const color = getQueryColor(queryName);
194
+ queryColorCache.set(queryName, applyAlpha(color, alpha));
195
+ }
196
+ return queryColorCache.get(queryName);
197
+ };
198
+ mainCanvas.beginPath();
199
+ const offsets = view.views.map(v => v.offsetPx);
200
+ const offsetsL0 = offsets[level];
201
+ const offsetsL1 = offsets[level + 1];
202
+ const unitMultiplier = Math.floor(exports.MAX_COLOR_RANGE / featPositions.length);
203
+ const y1 = 0;
204
+ const y2 = height;
205
+ const mid = (y2 - y1) / 2;
206
+ const useStrandColorThin = colorBy === 'strand';
207
+ const useQueryColorThin = colorBy === 'query';
208
+ mainCanvas.fillStyle = colorMapWithAlpha.M;
209
+ mainCanvas.strokeStyle = colorMapWithAlpha.M;
210
+ const thinLinesByColor = new Map();
211
+ for (const { p11, p12, p21, p22, f } of featPositions) {
212
+ if (minAlignmentLength > 0) {
213
+ const queryName = f.get('name') || f.get('id') || f.id();
214
+ const totalLength = queryTotalLengths.get(queryName) || 0;
215
+ if (totalLength < minAlignmentLength) {
216
+ continue;
217
+ }
218
+ }
219
+ const x11 = p11.offsetPx - offsetsL0;
220
+ const x12 = p12.offsetPx - offsetsL0;
221
+ const x21 = p21.offsetPx - offsetsL1;
222
+ const x22 = p22.offsetPx - offsetsL1;
223
+ const l1 = Math.abs(x12 - x11);
224
+ const l2 = Math.abs(x22 - x21);
56
225
  if (l1 <= lineLimit &&
57
226
  l2 <= lineLimit &&
58
227
  x21 < width + oobLimit &&
59
228
  x21 > -oobLimit) {
60
- ctx1.moveTo(x11, y1);
61
- if (drawCurves) {
62
- ctx1.bezierCurveTo(x11, mid, x21, mid, x21, y2);
229
+ let colorKey = 'default';
230
+ if (useStrandColorThin) {
231
+ const strand = f.get('strand');
232
+ colorKey = strand === -1 ? 'neg' : 'pos';
63
233
  }
64
- else {
65
- ctx1.lineTo(x21, y2);
234
+ else if (useQueryColorThin) {
235
+ colorKey = f.get('refName');
236
+ }
237
+ if (!thinLinesByColor.has(colorKey)) {
238
+ thinLinesByColor.set(colorKey, []);
66
239
  }
240
+ thinLinesByColor.get(colorKey).push({ x11, x21, y1, y2, mid });
67
241
  }
68
242
  }
69
- ctx1.stroke();
70
- ctx1.fillStyle = colorMap.M;
71
- ctx1.strokeStyle = colorMap.M;
243
+ for (const [colorKey, lines] of thinLinesByColor) {
244
+ if (colorKey === 'pos') {
245
+ mainCanvas.strokeStyle = posColorWithAlpha;
246
+ }
247
+ else if (colorKey === 'neg') {
248
+ mainCanvas.strokeStyle = negColorWithAlpha;
249
+ }
250
+ else if (colorKey !== 'default') {
251
+ mainCanvas.strokeStyle = getQueryColorWithAlpha(colorKey);
252
+ }
253
+ else {
254
+ mainCanvas.strokeStyle = colorMapWithAlpha.M;
255
+ }
256
+ mainCanvas.beginPath();
257
+ if (drawCurves) {
258
+ for (const { x11, x21, y1, y2, mid } of lines) {
259
+ mainCanvas.moveTo(x11, y1);
260
+ mainCanvas.bezierCurveTo(x11, mid, x21, mid, x21, y2);
261
+ }
262
+ }
263
+ else {
264
+ for (const { x11, x21, y1, y2 } of lines) {
265
+ mainCanvas.moveTo(x11, y1);
266
+ mainCanvas.lineTo(x21, y2);
267
+ }
268
+ }
269
+ mainCanvas.stroke();
270
+ }
271
+ const bpPerPx0 = bpPerPxs[level];
272
+ const bpPerPx1 = bpPerPxs[level + 1];
273
+ const bpPerPxInv0 = 1 / bpPerPx0;
274
+ const bpPerPxInv1 = 1 / bpPerPx1;
275
+ const useStrandColor = colorBy === 'strand';
276
+ const useQueryColor = colorBy === 'query';
277
+ mainCanvas.fillStyle = colorMapWithAlpha.M;
278
+ mainCanvas.strokeStyle = colorMapWithAlpha.M;
72
279
  for (const { p11, p12, p21, p22, f, cigar } of featPositions) {
73
- const x11 = p11.offsetPx - offsets[level];
74
- const x12 = p12.offsetPx - offsets[level];
75
- const x21 = p21.offsetPx - offsets[level + 1];
76
- const x22 = p22.offsetPx - offsets[level + 1];
280
+ const strand = f.get('strand');
281
+ const refName = f.get('refName');
282
+ if (minAlignmentLength > 0) {
283
+ const queryName = f.get('name') || f.get('id') || f.id();
284
+ const totalLength = queryTotalLengths.get(queryName) || 0;
285
+ if (totalLength < minAlignmentLength) {
286
+ continue;
287
+ }
288
+ }
289
+ const x11 = p11.offsetPx - offsetsL0;
290
+ const x12 = p12.offsetPx - offsetsL0;
291
+ const x21 = p21.offsetPx - offsetsL1;
292
+ const x22 = p22.offsetPx - offsetsL1;
77
293
  const l1 = Math.abs(x12 - x11);
78
294
  const l2 = Math.abs(x22 - x21);
79
295
  const minX = Math.min(x21, x22);
80
296
  const maxX = Math.max(x21, x22);
81
- const y1 = 0;
82
- const y2 = height;
83
- const mid = (y2 - y1) / 2;
84
297
  if (!(l1 <= lineLimit && l2 <= lineLimit) &&
85
298
  (0, util_1.doesIntersect2)(minX, maxX, -oobLimit, view.width + oobLimit)) {
86
- const s1 = f.get('strand');
299
+ const s1 = strand;
87
300
  const k1 = s1 === -1 ? x12 : x11;
88
301
  const k2 = s1 === -1 ? x11 : x12;
89
302
  const rev1 = k1 < k2 ? 1 : -1;
@@ -94,17 +307,15 @@ function drawRef(model, ctx1, ctx3) {
94
307
  let continuingFlag = false;
95
308
  let px1 = 0;
96
309
  let px2 = 0;
97
- const unitMultiplier2 = Math.floor(exports.MAX_COLOR_RANGE / cigar.length);
98
310
  for (let j = 0; j < cigar.length; j += 2) {
99
- const idx = j * unitMultiplier2 + 1;
100
311
  const len = +cigar[j];
101
312
  const op = cigar[j + 1];
102
313
  if (!continuingFlag) {
103
314
  px1 = cx1;
104
315
  px2 = cx2;
105
316
  }
106
- const d1 = len / bpPerPxs[level];
107
- const d2 = len / bpPerPxs[level + 1];
317
+ const d1 = len * bpPerPxInv0;
318
+ const d2 = len * bpPerPxInv1;
108
319
  if (op === 'M' || op === '=' || op === 'X') {
109
320
  cx1 += d1 * rev1;
110
321
  cx2 += d2 * rev2;
@@ -125,30 +336,51 @@ function drawRef(model, ctx1, ctx3) {
125
336
  }
126
337
  else {
127
338
  const letter = (continuingFlag && d1 > 1) || d2 > 1 ? op : 'M';
128
- ctx1.fillStyle = colorMap[letter];
339
+ const isInsertionOrDeletion = letter === 'I' || letter === 'D' || letter === 'N';
340
+ if (useStrandColor && !isInsertionOrDeletion) {
341
+ mainCanvas.fillStyle =
342
+ strand === -1 ? negColorWithAlpha : posColorWithAlpha;
343
+ }
344
+ else if (useQueryColor && !isInsertionOrDeletion) {
345
+ mainCanvas.fillStyle = getQueryColorWithAlpha(refName);
346
+ }
347
+ else {
348
+ mainCanvas.fillStyle = colorMapWithAlpha[letter];
349
+ }
129
350
  continuingFlag = false;
130
351
  if (drawCIGARMatchesOnly) {
131
352
  if (letter === 'M') {
132
- (0, util_2.draw)(ctx1, px1, cx1, y1, cx2, px2, y2, mid, drawCurves);
133
- ctx1.fill();
353
+ (0, util_2.draw)(mainCanvas, px1, cx1, y1, cx2, px2, y2, mid, drawCurves);
354
+ mainCanvas.fill();
355
+ if (drawLocationMarkersEnabled) {
356
+ (0, util_2.drawLocationMarkers)(mainCanvas, px1, cx1, y1, cx2, px2, y2, mid, bpPerPx0, bpPerPx1, drawCurves);
357
+ }
134
358
  }
135
359
  }
136
360
  else {
137
- (0, util_2.draw)(ctx1, px1, cx1, y1, cx2, px2, y2, mid, drawCurves);
138
- ctx1.fill();
139
- }
140
- if (ctx3) {
141
- ctx3.fillStyle = makeColor(idx);
142
- (0, util_2.draw)(ctx3, px1, cx1, y1, cx2, px2, y2, mid, drawCurves);
143
- ctx3.fill();
361
+ (0, util_2.draw)(mainCanvas, px1, cx1, y1, cx2, px2, y2, mid, drawCurves);
362
+ mainCanvas.fill();
363
+ if (drawLocationMarkersEnabled) {
364
+ (0, util_2.drawLocationMarkers)(mainCanvas, px1, cx1, y1, cx2, px2, y2, mid, bpPerPx0, bpPerPx1, drawCurves);
365
+ }
144
366
  }
145
367
  }
146
368
  }
147
369
  }
148
370
  }
149
371
  else {
150
- (0, util_2.draw)(ctx1, x11, x12, y1, x22, x21, y2, mid, drawCurves);
151
- ctx1.fill();
372
+ if (useStrandColor) {
373
+ mainCanvas.fillStyle =
374
+ strand === -1 ? negColorWithAlpha : posColorWithAlpha;
375
+ }
376
+ else if (useQueryColor) {
377
+ mainCanvas.fillStyle = getQueryColorWithAlpha(refName);
378
+ }
379
+ (0, util_2.draw)(mainCanvas, x11, x12, y1, x22, x21, y2, mid, drawCurves);
380
+ mainCanvas.fill();
381
+ if (useStrandColor || useQueryColor) {
382
+ mainCanvas.fillStyle = colorMapWithAlpha.M;
383
+ }
152
384
  }
153
385
  }
154
386
  }
@@ -160,6 +392,13 @@ function drawRef(model, ctx1, ctx3) {
160
392
  ctx2.clearRect(0, 0, width, height);
161
393
  for (let i = 0; i < featPositions.length; i++) {
162
394
  const feature = featPositions[i];
395
+ if (minAlignmentLength > 0) {
396
+ const queryName = feature.f.get('name') || feature.f.get('id') || feature.f.id();
397
+ const totalLength = queryTotalLengths.get(queryName) || 0;
398
+ if (totalLength < minAlignmentLength) {
399
+ continue;
400
+ }
401
+ }
163
402
  const idx = i * unitMultiplier + 1;
164
403
  ctx2.fillStyle = makeColor(idx);
165
404
  (0, util_2.drawMatchSimple)({
@@ -178,7 +417,7 @@ function drawRef(model, ctx1, ctx3) {
178
417
  });
179
418
  }
180
419
  }
181
- function drawMouseoverSynteny(model) {
420
+ function drawMouseoverClickMap(model) {
182
421
  var _a;
183
422
  const { level, clickId, mouseoverId } = model;
184
423
  const highResolutionScaling = 1;
@@ -22,6 +22,7 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
22
22
  } & {
23
23
  type: import("mobx-state-tree").ISimpleType<"LinearSyntenyDisplay">;
24
24
  configuration: AnyConfigurationSchemaType;
25
+ colorBy: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
25
26
  }, {
26
27
  rendererTypeName: string;
27
28
  error: unknown;
@@ -260,6 +261,8 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
260
261
  mouseoverId: string | undefined;
261
262
  clickId: string | undefined;
262
263
  cigarMouseoverId: number;
264
+ alpha: number;
265
+ minAlignmentLength: number;
263
266
  } & {
264
267
  setFeatPositions(arg: FeatPos[]): void;
265
268
  setMainCanvasRef(ref: HTMLCanvasElement | null): void;
@@ -269,6 +272,9 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
269
272
  setMouseoverId(arg?: string): void;
270
273
  setCigarMouseoverId(arg: number): void;
271
274
  setClickId(arg?: string): void;
275
+ setAlpha(value: number): void;
276
+ setMinAlignmentLength(value: number): void;
277
+ setColorBy(value: string): void;
272
278
  } & {
273
279
  readonly adapterConfig: any;
274
280
  readonly trackIds: string[];
@@ -44,6 +44,7 @@ function stateModelFactory(configSchema) {
44
44
  .compose('LinearSyntenyDisplay', (0, stateModelFactory_1.default)(configSchema), mobx_state_tree_1.types.model({
45
45
  type: mobx_state_tree_1.types.literal('LinearSyntenyDisplay'),
46
46
  configuration: (0, configuration_1.ConfigurationReference)(configSchema),
47
+ colorBy: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.string, 'default'),
47
48
  }))
48
49
  .volatile(() => ({
49
50
  mainCanvas: null,
@@ -54,6 +55,8 @@ function stateModelFactory(configSchema) {
54
55
  mouseoverId: undefined,
55
56
  clickId: undefined,
56
57
  cigarMouseoverId: -1,
58
+ alpha: 0.2,
59
+ minAlignmentLength: 0,
57
60
  }))
58
61
  .actions(self => ({
59
62
  setFeatPositions(arg) {
@@ -80,6 +83,15 @@ function stateModelFactory(configSchema) {
80
83
  setClickId(arg) {
81
84
  self.clickId = arg;
82
85
  },
86
+ setAlpha(value) {
87
+ self.alpha = value;
88
+ },
89
+ setMinAlignmentLength(value) {
90
+ self.minAlignmentLength = value;
91
+ },
92
+ setColorBy(value) {
93
+ self.colorBy = value;
94
+ },
83
95
  }))
84
96
  .views(self => ({
85
97
  get adapterConfig() {
@@ -0,0 +1,6 @@
1
+ import type { LinearSyntenyViewModel } from '../model';
2
+ declare const DiagonalizationProgressDialog: ({ handleClose, model, }: {
3
+ handleClose: () => void;
4
+ model: LinearSyntenyViewModel;
5
+ }) => import("react/jsx-runtime").JSX.Element;
6
+ export default DiagonalizationProgressDialog;
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const jsx_runtime_1 = require("react/jsx-runtime");
4
+ const react_1 = require("react");
5
+ const ui_1 = require("@jbrowse/core/ui");
6
+ const util_1 = require("@jbrowse/core/util");
7
+ const material_1 = require("@mui/material");
8
+ const mobx_1 = require("mobx");
9
+ const mobx_react_1 = require("mobx-react");
10
+ const diagonalize_1 = require("../util/diagonalize");
11
+ const DiagonalizationProgressDialog = (0, mobx_react_1.observer)(function ({ handleClose, model, }) {
12
+ const [progress, setProgress] = (0, react_1.useState)(0);
13
+ const [message, setMessage] = (0, react_1.useState)('Initializing...');
14
+ const [error, setError] = (0, react_1.useState)();
15
+ (0, react_1.useEffect)(() => {
16
+ const runDiagonalization = async () => {
17
+ const session = (0, util_1.getSession)(model);
18
+ try {
19
+ if (model.views.length !== 2) {
20
+ setError('Diagonalization requires exactly 2 views');
21
+ setProgress(100);
22
+ session.notify('Diagonalization requires exactly 2 views', 'warning');
23
+ return;
24
+ }
25
+ const queryView = model.views[1];
26
+ setProgress(5);
27
+ setMessage('Collecting alignment data...');
28
+ const alignments = [];
29
+ for (const level of model.levels) {
30
+ for (const track of level.tracks) {
31
+ for (const display of track.displays) {
32
+ const { featPositions } = display;
33
+ for (const { f } of featPositions) {
34
+ const mate = f.get('mate');
35
+ alignments.push({
36
+ queryRefName: f.get('refName'),
37
+ refRefName: mate.refName,
38
+ queryStart: f.get('start'),
39
+ queryEnd: f.get('end'),
40
+ refStart: mate.start,
41
+ refEnd: mate.end,
42
+ strand: f.get('strand') || 1,
43
+ });
44
+ }
45
+ }
46
+ }
47
+ }
48
+ if (alignments.length === 0) {
49
+ setError('No alignments found');
50
+ setProgress(100);
51
+ session.notify('No alignments found to diagonalize', 'warning');
52
+ return;
53
+ }
54
+ const result = await (0, diagonalize_1.diagonalizeRegions)(alignments, queryView.displayedRegions, async (prog, msg) => {
55
+ setProgress(prog);
56
+ setMessage(msg);
57
+ });
58
+ if (result.newRegions.length > 0) {
59
+ setProgress(95);
60
+ setMessage('Applying new layout...');
61
+ (0, mobx_1.transaction)(() => {
62
+ queryView.setDisplayedRegions(result.newRegions);
63
+ });
64
+ setProgress(100);
65
+ setMessage('Diagonalization complete');
66
+ setTimeout(() => {
67
+ handleClose();
68
+ }, 1500);
69
+ }
70
+ else {
71
+ setError('No regions to reorder');
72
+ setProgress(100);
73
+ session.notify('No query regions found to reorder', 'warning');
74
+ }
75
+ }
76
+ catch (err) {
77
+ console.error('Diagonalization error:', err);
78
+ setError(`Error: ${err}`);
79
+ setProgress(100);
80
+ session.notify(`Diagonalization failed: ${err}`, 'error');
81
+ }
82
+ };
83
+ runDiagonalization();
84
+ }, [model, handleClose]);
85
+ return ((0, jsx_runtime_1.jsxs)(ui_1.Dialog, { open: true, title: "Diagonalizing", onClose: handleClose, children: [(0, jsx_runtime_1.jsxs)(material_1.DialogContent, { style: { minWidth: 400 }, children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "body1", gutterBottom: true, color: error ? 'error' : 'inherit', children: error || message }), (0, jsx_runtime_1.jsx)(material_1.LinearProgress, { variant: "determinate", value: progress, style: { marginTop: 16 }, color: error ? 'error' : 'primary' }), (0, jsx_runtime_1.jsxs)(material_1.Typography, { variant: "caption", color: "textSecondary", style: { marginTop: 8, display: 'block' }, children: [Math.round(progress), "% complete"] })] }), (0, jsx_runtime_1.jsx)(material_1.DialogActions, { children: (0, jsx_runtime_1.jsx)(material_1.Button, { onClick: handleClose, color: "primary", disabled: progress < 100, children: progress < 100 ? 'Processing...' : 'Done' }) })] }));
86
+ });
87
+ exports.default = DiagonalizationProgressDialog;