@jbrowse/plugin-linear-comparative-view 3.6.5 → 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.
- package/dist/LGVSyntenyDisplay/model.d.ts +17 -2
- package/dist/LinearComparativeView/components/ColorBySelector.d.ts +5 -0
- package/dist/LinearComparativeView/components/ColorBySelector.js +54 -0
- package/dist/LinearComparativeView/components/Header.js +7 -2
- package/dist/LinearComparativeView/components/MinLengthSlider.d.ts +5 -0
- package/dist/LinearComparativeView/components/MinLengthSlider.js +47 -0
- package/dist/LinearComparativeView/components/OpacitySlider.d.ts +5 -0
- package/dist/LinearComparativeView/components/OpacitySlider.js +46 -0
- package/dist/LinearComparativeView/components/RubberbandSpan.js +2 -2
- package/dist/LinearComparativeView/components/SliderTooltip.d.ts +2 -0
- package/dist/LinearComparativeView/components/SliderTooltip.js +9 -0
- package/dist/LinearComparativeView/components/useRangeSelect.js +10 -14
- package/dist/LinearComparativeView/model.d.ts +3 -0
- package/dist/LinearComparativeView/model.js +4 -0
- package/dist/LinearSyntenyDisplay/afterAttach.js +5 -3
- package/dist/LinearSyntenyDisplay/components/LinearSyntenyRendering.js +10 -5
- package/dist/LinearSyntenyDisplay/components/util.d.ts +2 -1
- package/dist/LinearSyntenyDisplay/components/util.js +43 -2
- package/dist/LinearSyntenyDisplay/drawSynteny.d.ts +3 -2
- package/dist/LinearSyntenyDisplay/drawSynteny.js +284 -45
- package/dist/LinearSyntenyDisplay/model.d.ts +6 -0
- package/dist/LinearSyntenyDisplay/model.js +12 -0
- package/dist/LinearSyntenyView/components/DiagonalizationProgressDialog.d.ts +6 -0
- package/dist/LinearSyntenyView/components/DiagonalizationProgressDialog.js +87 -0
- package/dist/LinearSyntenyView/model.d.ts +42 -11
- package/dist/LinearSyntenyView/model.js +70 -18
- package/dist/LinearSyntenyView/util/diagonalize.d.ts +27 -0
- package/dist/LinearSyntenyView/util/diagonalize.js +91 -0
- package/esm/LGVSyntenyDisplay/model.d.ts +17 -2
- package/esm/LinearComparativeView/components/ColorBySelector.d.ts +5 -0
- package/esm/LinearComparativeView/components/ColorBySelector.js +49 -0
- package/esm/LinearComparativeView/components/Header.js +8 -3
- package/esm/LinearComparativeView/components/MinLengthSlider.d.ts +5 -0
- package/esm/LinearComparativeView/components/MinLengthSlider.js +42 -0
- package/esm/LinearComparativeView/components/OpacitySlider.d.ts +5 -0
- package/esm/LinearComparativeView/components/OpacitySlider.js +41 -0
- package/esm/LinearComparativeView/components/RubberbandSpan.js +2 -2
- package/esm/LinearComparativeView/components/SliderTooltip.d.ts +2 -0
- package/esm/LinearComparativeView/components/SliderTooltip.js +6 -0
- package/esm/LinearComparativeView/components/useRangeSelect.js +11 -15
- package/esm/LinearComparativeView/model.d.ts +3 -0
- package/esm/LinearComparativeView/model.js +4 -0
- package/esm/LinearSyntenyDisplay/afterAttach.js +6 -4
- package/esm/LinearSyntenyDisplay/components/LinearSyntenyRendering.js +10 -5
- package/esm/LinearSyntenyDisplay/components/util.d.ts +2 -1
- package/esm/LinearSyntenyDisplay/components/util.js +43 -3
- package/esm/LinearSyntenyDisplay/drawSynteny.d.ts +3 -2
- package/esm/LinearSyntenyDisplay/drawSynteny.js +283 -45
- package/esm/LinearSyntenyDisplay/model.d.ts +6 -0
- package/esm/LinearSyntenyDisplay/model.js +12 -0
- package/esm/LinearSyntenyView/components/DiagonalizationProgressDialog.d.ts +6 -0
- package/esm/LinearSyntenyView/components/DiagonalizationProgressDialog.js +85 -0
- package/esm/LinearSyntenyView/model.d.ts +42 -11
- package/esm/LinearSyntenyView/model.js +70 -18
- package/esm/LinearSyntenyView/util/diagonalize.d.ts +27 -0
- package/esm/LinearSyntenyView/util/diagonalize.js +88 -0
- package/package.json +5 -5
|
@@ -13,6 +13,7 @@ export default function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
13
13
|
showIntraviewLinks: import("mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
|
|
14
14
|
linkViews: import("mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
|
|
15
15
|
interactiveOverlay: import("mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
|
|
16
|
+
showDynamicControls: import("mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
|
|
16
17
|
levels: import("mobx-state-tree").IArrayType<import("mobx-state-tree").IModelType<{
|
|
17
18
|
id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
|
|
18
19
|
type: import("mobx-state-tree").IType<string | undefined, string, string>;
|
|
@@ -285,6 +286,7 @@ export default function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
285
286
|
drawCIGAR: import("mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
|
|
286
287
|
drawCIGARMatchesOnly: import("mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
|
|
287
288
|
drawCurves: import("mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
|
|
289
|
+
drawLocationMarkers: import("mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
|
|
288
290
|
}, {
|
|
289
291
|
width: number;
|
|
290
292
|
} & {
|
|
@@ -308,6 +310,7 @@ export default function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
308
310
|
removeView(view: import("@jbrowse/plugin-linear-genome-view").LinearGenomeViewModel): void;
|
|
309
311
|
setLevelHeight(newHeight: number, level?: number): number;
|
|
310
312
|
setLinkViews(arg: boolean): void;
|
|
313
|
+
setShowDynamicControls(arg: boolean): void;
|
|
311
314
|
activateTrackSelector(level: number): import("@jbrowse/core/util").Widget;
|
|
312
315
|
toggleTrack(trackId: string, level?: number): void;
|
|
313
316
|
showTrack(trackId: string, level?: number, initialSnapshot?: {}): void;
|
|
@@ -333,6 +336,7 @@ export default function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
333
336
|
setDrawCurves(arg: boolean): void;
|
|
334
337
|
setDrawCIGAR(arg: boolean): void;
|
|
335
338
|
setDrawCIGARMatchesOnly(arg: boolean): void;
|
|
339
|
+
setDrawLocationMarkers(arg: boolean): void;
|
|
336
340
|
showAllRegions(): void;
|
|
337
341
|
} & {
|
|
338
342
|
exportSvg(opts: ExportSvgOptions): Promise<void>;
|
|
@@ -344,40 +348,66 @@ export default function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
344
348
|
icon: import("@mui/material/OverridableComponent").OverridableComponent<import("@mui/material").SvgIconTypeMap<{}, "svg">> & {
|
|
345
349
|
muiName: string;
|
|
346
350
|
};
|
|
347
|
-
|
|
351
|
+
helpText: string;
|
|
348
352
|
type?: undefined;
|
|
353
|
+
checked?: undefined;
|
|
354
|
+
subMenu?: undefined;
|
|
349
355
|
} | {
|
|
350
356
|
label: string;
|
|
351
|
-
checked: boolean;
|
|
352
357
|
type: string;
|
|
353
|
-
|
|
358
|
+
checked: boolean;
|
|
354
359
|
onClick: () => void;
|
|
360
|
+
helpText: string;
|
|
361
|
+
description?: undefined;
|
|
355
362
|
icon?: undefined;
|
|
363
|
+
subMenu?: undefined;
|
|
356
364
|
} | {
|
|
357
365
|
label: string;
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
366
|
+
subMenu: ({
|
|
367
|
+
label: string;
|
|
368
|
+
checked: boolean;
|
|
369
|
+
type: string;
|
|
370
|
+
description: string;
|
|
371
|
+
onClick: () => void;
|
|
372
|
+
helpText: string;
|
|
373
|
+
icon?: undefined;
|
|
374
|
+
} | {
|
|
375
|
+
label: string;
|
|
376
|
+
type: string;
|
|
377
|
+
checked: boolean;
|
|
378
|
+
icon: typeof Curves;
|
|
379
|
+
onClick: () => void;
|
|
380
|
+
helpText: string;
|
|
381
|
+
description?: undefined;
|
|
382
|
+
})[];
|
|
383
|
+
onClick?: undefined;
|
|
364
384
|
description?: undefined;
|
|
385
|
+
icon?: undefined;
|
|
386
|
+
helpText?: undefined;
|
|
387
|
+
type?: undefined;
|
|
388
|
+
checked?: undefined;
|
|
365
389
|
} | {
|
|
366
390
|
label: string;
|
|
367
391
|
type: string;
|
|
368
392
|
checked: boolean;
|
|
369
|
-
icon:
|
|
393
|
+
icon: import("@mui/material/OverridableComponent").OverridableComponent<import("@mui/material").SvgIconTypeMap<{}, "svg">> & {
|
|
394
|
+
muiName: string;
|
|
395
|
+
};
|
|
370
396
|
onClick: () => void;
|
|
397
|
+
helpText: string;
|
|
371
398
|
description?: undefined;
|
|
399
|
+
subMenu?: undefined;
|
|
372
400
|
} | {
|
|
373
401
|
label: string;
|
|
374
402
|
icon: import("@mui/material/OverridableComponent").OverridableComponent<import("@mui/material").SvgIconTypeMap<{}, "svg">> & {
|
|
375
403
|
muiName: string;
|
|
376
404
|
};
|
|
377
405
|
onClick: () => void;
|
|
406
|
+
helpText: string;
|
|
378
407
|
description?: undefined;
|
|
379
|
-
checked?: undefined;
|
|
380
408
|
type?: undefined;
|
|
409
|
+
checked?: undefined;
|
|
410
|
+
subMenu?: undefined;
|
|
381
411
|
})[];
|
|
382
412
|
menuItems(): (import("@jbrowse/core/ui").MenuDivider | import("@jbrowse/core/ui").MenuSubHeader | import("@jbrowse/core/ui").NormalMenuItem | import("@jbrowse/core/ui").CheckboxMenuItem | import("@jbrowse/core/ui").RadioMenuItem | import("@jbrowse/core/ui").SubMenuItem | {
|
|
383
413
|
label: string;
|
|
@@ -399,6 +429,7 @@ export default function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
399
429
|
showIntraviewLinks: import("mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
|
|
400
430
|
linkViews: import("mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
|
|
401
431
|
interactiveOverlay: import("mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
|
|
432
|
+
showDynamicControls: import("mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
|
|
402
433
|
levels: import("mobx-state-tree").IArrayType<import("mobx-state-tree").IModelType<{
|
|
403
434
|
id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
|
|
404
435
|
type: import("mobx-state-tree").IType<string | undefined, string, string>;
|
|
@@ -3,6 +3,7 @@ import { getSession } from '@jbrowse/core/util';
|
|
|
3
3
|
import CropFreeIcon from '@mui/icons-material/CropFree';
|
|
4
4
|
import LinkIcon from '@mui/icons-material/Link';
|
|
5
5
|
import PhotoCameraIcon from '@mui/icons-material/PhotoCamera';
|
|
6
|
+
import ShuffleIcon from '@mui/icons-material/Shuffle';
|
|
6
7
|
import VisibilityIcon from '@mui/icons-material/Visibility';
|
|
7
8
|
import { saveAs } from 'file-saver';
|
|
8
9
|
import { observable, transaction } from 'mobx';
|
|
@@ -10,6 +11,7 @@ import { types } from 'mobx-state-tree';
|
|
|
10
11
|
import { Curves } from './components/Icons';
|
|
11
12
|
import baseModel from '../LinearComparativeView/model';
|
|
12
13
|
const ExportSvgDialog = lazy(() => import('./components/ExportSvgDialog'));
|
|
14
|
+
const DiagonalizationProgressDialog = lazy(() => import('./components/DiagonalizationProgressDialog'));
|
|
13
15
|
export default function stateModelFactory(pluginManager) {
|
|
14
16
|
return types
|
|
15
17
|
.compose('LinearSyntenyView', baseModel(pluginManager), types.model({
|
|
@@ -17,6 +19,7 @@ export default function stateModelFactory(pluginManager) {
|
|
|
17
19
|
drawCIGAR: true,
|
|
18
20
|
drawCIGARMatchesOnly: false,
|
|
19
21
|
drawCurves: false,
|
|
22
|
+
drawLocationMarkers: false,
|
|
20
23
|
}))
|
|
21
24
|
.volatile(() => ({
|
|
22
25
|
importFormSyntenyTrackSelections: observable.array(),
|
|
@@ -40,6 +43,9 @@ export default function stateModelFactory(pluginManager) {
|
|
|
40
43
|
setDrawCIGARMatchesOnly(arg) {
|
|
41
44
|
self.drawCIGARMatchesOnly = arg;
|
|
42
45
|
},
|
|
46
|
+
setDrawLocationMarkers(arg) {
|
|
47
|
+
self.drawLocationMarkers = arg;
|
|
48
|
+
},
|
|
43
49
|
showAllRegions() {
|
|
44
50
|
transaction(() => {
|
|
45
51
|
for (const view of self.views) {
|
|
@@ -68,30 +74,83 @@ export default function stateModelFactory(pluginManager) {
|
|
|
68
74
|
onClick: self.squareView,
|
|
69
75
|
description: 'Makes both views use the same zoom level, adjusting to the average of each',
|
|
70
76
|
icon: CropFreeIcon,
|
|
77
|
+
helpText: 'Square view synchronizes the zoom levels of both genome views by calculating the average zoom level and applying it to both panels. This helps ensure features are displayed at comparable scales, making it easier to compare syntenic regions visually.',
|
|
71
78
|
},
|
|
72
79
|
{
|
|
73
80
|
label: 'Show all regions',
|
|
74
81
|
onClick: self.showAllRegions,
|
|
75
82
|
description: 'Show entire genome assemblies',
|
|
76
83
|
icon: VisibilityIcon,
|
|
84
|
+
helpText: 'This command will zoom out all views to display the entire genome assemblies. This is useful when you want to get a high-level overview of syntenic relationships across whole genomes or when you need to reset the view after zooming into specific regions.',
|
|
77
85
|
},
|
|
78
86
|
{
|
|
79
|
-
label: '
|
|
80
|
-
checked: self.drawCIGAR,
|
|
81
|
-
type: 'checkbox',
|
|
82
|
-
description: 'If disabled, only draws the broad scale CIGAR match',
|
|
87
|
+
label: 'Re-order chromosomes',
|
|
83
88
|
onClick: () => {
|
|
84
|
-
self.
|
|
89
|
+
getSession(self).queueDialog(handleClose => [
|
|
90
|
+
DiagonalizationProgressDialog,
|
|
91
|
+
{
|
|
92
|
+
handleClose,
|
|
93
|
+
model: self,
|
|
94
|
+
},
|
|
95
|
+
]);
|
|
85
96
|
},
|
|
97
|
+
icon: ShuffleIcon,
|
|
98
|
+
description: "Reorder and reorient query regions to minimize crossing lines, also known as 'diagonalizing'",
|
|
99
|
+
helpText: "This operation 'diagonalizes' the data which algorithmically reorders and reorients chromosomes to minimize crossing synteny lines, creating a more diagonal pattern. This makes it easier to identify large-scale genomic rearrangements, inversions, and translocations. The process may take a few moments for large genomes.",
|
|
86
100
|
},
|
|
87
101
|
{
|
|
88
|
-
label: '
|
|
89
|
-
checked: self.drawCIGARMatchesOnly,
|
|
102
|
+
label: 'Show dynamic controls',
|
|
90
103
|
type: 'checkbox',
|
|
91
|
-
|
|
104
|
+
checked: self.showDynamicControls,
|
|
92
105
|
onClick: () => {
|
|
93
|
-
self.
|
|
106
|
+
self.setShowDynamicControls(!self.showDynamicControls);
|
|
94
107
|
},
|
|
108
|
+
helpText: 'Toggle visibility of dynamic controls like opacity and minimum length sliders. These controls allow you to adjust synteny visualization parameters in real-time.',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
label: 'Draw',
|
|
112
|
+
subMenu: [
|
|
113
|
+
{
|
|
114
|
+
label: 'Draw CIGAR',
|
|
115
|
+
checked: self.drawCIGAR,
|
|
116
|
+
type: 'checkbox',
|
|
117
|
+
description: 'If disabled, only draws the broad scale CIGAR match',
|
|
118
|
+
onClick: () => {
|
|
119
|
+
self.setDrawCIGAR(!self.drawCIGAR);
|
|
120
|
+
},
|
|
121
|
+
helpText: 'CIGAR strings encode detailed alignment information including matches, insertions, and deletions. When enabled, this option visualizes the fine-scale variations in syntenic alignments. Disable this for a cleaner view that shows only broad syntenic blocks.',
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
label: 'Draw only CIGAR matches',
|
|
125
|
+
checked: self.drawCIGARMatchesOnly,
|
|
126
|
+
type: 'checkbox',
|
|
127
|
+
description: 'If enabled, it hides the insertions and deletions in the CIGAR strings, helps with divergent',
|
|
128
|
+
onClick: () => {
|
|
129
|
+
self.setDrawCIGARMatchesOnly(!self.drawCIGARMatchesOnly);
|
|
130
|
+
},
|
|
131
|
+
helpText: 'When comparing divergent genomes, showing all insertions and deletions can clutter the view. This option filters the CIGAR visualization to show only the matching regions, providing a cleaner view of conserved syntenic blocks while hiding small-scale indels.',
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
label: 'Draw curved lines',
|
|
135
|
+
type: 'checkbox',
|
|
136
|
+
checked: self.drawCurves,
|
|
137
|
+
icon: Curves,
|
|
138
|
+
onClick: () => {
|
|
139
|
+
self.setDrawCurves(!self.drawCurves);
|
|
140
|
+
},
|
|
141
|
+
helpText: 'Toggle between straight lines and smooth bezier curves for synteny connections. Curved lines can make the visualization more aesthetically pleasing and may help reduce visual clutter when many syntenic regions are displayed. Straight lines provide a more direct representation.',
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
label: 'Draw location markers',
|
|
145
|
+
type: 'checkbox',
|
|
146
|
+
checked: self.drawLocationMarkers,
|
|
147
|
+
description: 'Draw periodic markers to show location within large matches',
|
|
148
|
+
onClick: () => {
|
|
149
|
+
self.setDrawLocationMarkers(!self.drawLocationMarkers);
|
|
150
|
+
},
|
|
151
|
+
helpText: 'Location markers add periodic visual indicators along long syntenic blocks, helping you track position and scale within large conserved regions. This is particularly useful when examining very long syntenic matches where it can be difficult to gauge relative position.',
|
|
152
|
+
},
|
|
153
|
+
],
|
|
95
154
|
},
|
|
96
155
|
{
|
|
97
156
|
label: 'Link views',
|
|
@@ -101,15 +160,7 @@ export default function stateModelFactory(pluginManager) {
|
|
|
101
160
|
onClick: () => {
|
|
102
161
|
self.setLinkViews(!self.linkViews);
|
|
103
162
|
},
|
|
104
|
-
|
|
105
|
-
{
|
|
106
|
-
label: 'Use curved lines',
|
|
107
|
-
type: 'checkbox',
|
|
108
|
-
checked: self.drawCurves,
|
|
109
|
-
icon: Curves,
|
|
110
|
-
onClick: () => {
|
|
111
|
-
self.setDrawCurves(!self.drawCurves);
|
|
112
|
-
},
|
|
163
|
+
helpText: 'When linked, panning and zooming in one genome view will automatically adjust the other view to maintain the correspondence shown by synteny lines. This makes it easier to explore syntenic regions interactively. Unlink views to navigate each genome independently.',
|
|
113
164
|
},
|
|
114
165
|
{
|
|
115
166
|
label: 'Export SVG',
|
|
@@ -123,6 +174,7 @@ export default function stateModelFactory(pluginManager) {
|
|
|
123
174
|
},
|
|
124
175
|
]);
|
|
125
176
|
},
|
|
177
|
+
helpText: 'Export the current synteny view as a scalable vector graphics (SVG) file. SVG format preserves quality at any zoom level and can be edited in vector graphics software like Inkscape or Adobe Illustrator. Perfect for creating publication-quality figures.',
|
|
126
178
|
},
|
|
127
179
|
];
|
|
128
180
|
},
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface AlignmentData {
|
|
2
|
+
queryRefName: string;
|
|
3
|
+
refRefName: string;
|
|
4
|
+
queryStart: number;
|
|
5
|
+
queryEnd: number;
|
|
6
|
+
refStart: number;
|
|
7
|
+
refEnd: number;
|
|
8
|
+
strand: number;
|
|
9
|
+
}
|
|
10
|
+
export interface Region {
|
|
11
|
+
refName: string;
|
|
12
|
+
start: number;
|
|
13
|
+
end: number;
|
|
14
|
+
reversed?: boolean;
|
|
15
|
+
assemblyName: string;
|
|
16
|
+
}
|
|
17
|
+
export interface DiagonalizationResult {
|
|
18
|
+
newRegions: Region[];
|
|
19
|
+
stats: {
|
|
20
|
+
totalAlignments: number;
|
|
21
|
+
regionsProcessed: number;
|
|
22
|
+
regionsReordered: number;
|
|
23
|
+
regionsReversed: number;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export type ProgressCallback = (progress: number, message: string) => void | Promise<void>;
|
|
27
|
+
export declare function diagonalizeRegions(alignments: AlignmentData[], currentRegions: Region[], progressCallback?: ProgressCallback): Promise<DiagonalizationResult>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export async function diagonalizeRegions(alignments, currentRegions, progressCallback) {
|
|
2
|
+
const updateProgress = async (progress, message) => {
|
|
3
|
+
if (progressCallback) {
|
|
4
|
+
await progressCallback(progress, message);
|
|
5
|
+
}
|
|
6
|
+
};
|
|
7
|
+
await updateProgress(20, `Grouping ${alignments.length} alignments...`);
|
|
8
|
+
const queryGroups = new Map();
|
|
9
|
+
for (const aln of alignments) {
|
|
10
|
+
const targetRefName = aln.refRefName;
|
|
11
|
+
if (!queryGroups.has(targetRefName)) {
|
|
12
|
+
queryGroups.set(targetRefName, {
|
|
13
|
+
refAlignments: new Map(),
|
|
14
|
+
strandWeightedSum: 0,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
const group = queryGroups.get(targetRefName);
|
|
18
|
+
const alnLength = Math.abs(aln.queryEnd - aln.queryStart);
|
|
19
|
+
if (!group.refAlignments.has(aln.queryRefName)) {
|
|
20
|
+
group.refAlignments.set(aln.queryRefName, {
|
|
21
|
+
bases: 0,
|
|
22
|
+
positions: [],
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const refData = group.refAlignments.get(aln.queryRefName);
|
|
26
|
+
refData.bases += alnLength;
|
|
27
|
+
refData.positions.push((aln.queryStart + aln.queryEnd) / 2);
|
|
28
|
+
const direction = aln.strand >= 0 ? 1 : -1;
|
|
29
|
+
group.strandWeightedSum += direction * alnLength;
|
|
30
|
+
}
|
|
31
|
+
await updateProgress(50, 'Determining optimal ordering and orientation...');
|
|
32
|
+
const queryOrdering = [];
|
|
33
|
+
for (const [targetRefName, group] of queryGroups) {
|
|
34
|
+
let bestRefName = '';
|
|
35
|
+
let maxBases = 0;
|
|
36
|
+
let bestPositions = [];
|
|
37
|
+
for (const [firstViewRefName, data] of group.refAlignments) {
|
|
38
|
+
if (data.bases > maxBases) {
|
|
39
|
+
maxBases = data.bases;
|
|
40
|
+
bestRefName = firstViewRefName;
|
|
41
|
+
bestPositions = data.positions;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const bestRefPos = bestPositions.reduce((a, b) => a + b, 0) / bestPositions.length;
|
|
45
|
+
const shouldReverse = group.strandWeightedSum < 0;
|
|
46
|
+
queryOrdering.push({
|
|
47
|
+
refName: targetRefName,
|
|
48
|
+
bestRefName,
|
|
49
|
+
bestRefPos,
|
|
50
|
+
shouldReverse,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
await updateProgress(70, `Sorting ${queryOrdering.length} query regions...`);
|
|
54
|
+
queryOrdering.sort((a, b) => {
|
|
55
|
+
if (a.bestRefName !== b.bestRefName) {
|
|
56
|
+
return a.bestRefName.localeCompare(b.bestRefName);
|
|
57
|
+
}
|
|
58
|
+
return a.bestRefPos - b.bestRefPos;
|
|
59
|
+
});
|
|
60
|
+
await updateProgress(85, 'Building new region layout...');
|
|
61
|
+
const newQueryRegions = [];
|
|
62
|
+
let regionsReversed = 0;
|
|
63
|
+
for (const { refName, shouldReverse } of queryOrdering) {
|
|
64
|
+
const region = currentRegions.find(r => r.refName === refName);
|
|
65
|
+
if (region) {
|
|
66
|
+
newQueryRegions.push({
|
|
67
|
+
...region,
|
|
68
|
+
reversed: shouldReverse,
|
|
69
|
+
});
|
|
70
|
+
if (shouldReverse !== region.reversed) {
|
|
71
|
+
regionsReversed++;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
console.warn(`Could not find region for refName: ${refName}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
await updateProgress(100, 'Diagonalization complete!');
|
|
79
|
+
return {
|
|
80
|
+
newRegions: newQueryRegions,
|
|
81
|
+
stats: {
|
|
82
|
+
totalAlignments: alignments.length,
|
|
83
|
+
regionsProcessed: queryOrdering.length,
|
|
84
|
+
regionsReordered: newQueryRegions.length,
|
|
85
|
+
regionsReversed,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/plugin-linear-comparative-view",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.0",
|
|
4
4
|
"description": "JBrowse 2 linear comparative view",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jbrowse",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"clean": "rimraf dist esm *.tsbuildinfo"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@jbrowse/core": "^3.
|
|
40
|
-
"@jbrowse/plugin-alignments": "^3.
|
|
41
|
-
"@jbrowse/plugin-linear-genome-view": "^3.
|
|
39
|
+
"@jbrowse/core": "^3.7.0",
|
|
40
|
+
"@jbrowse/plugin-alignments": "^3.7.0",
|
|
41
|
+
"@jbrowse/plugin-linear-genome-view": "^3.7.0",
|
|
42
42
|
"@mui/icons-material": "^7.0.0",
|
|
43
43
|
"@mui/material": "^7.0.0",
|
|
44
44
|
"copy-to-clipboard": "^3.3.1",
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"publishConfig": {
|
|
60
60
|
"access": "public"
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "85bdd0d58286b7adbfd408146b15847676317635"
|
|
63
63
|
}
|