react-msaview 4.4.5 → 4.5.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/bundle/index.js +9 -9
- package/bundle/index.js.LICENSE.txt +8 -8
- package/bundle/index.js.map +1 -1
- package/dist/colorSchemes.d.ts +0 -6
- package/dist/colorSchemes.js +1 -119
- package/dist/colorSchemes.js.map +1 -1
- package/dist/components/ConservationTrack.d.ts +8 -0
- package/dist/components/ConservationTrack.js +54 -0
- package/dist/components/ConservationTrack.js.map +1 -0
- package/dist/components/Loading.js +14 -2
- package/dist/components/Loading.js.map +1 -1
- package/dist/components/MSAView.js +36 -0
- package/dist/components/MSAView.js.map +1 -1
- package/dist/components/SequenceTextArea.js +3 -2
- package/dist/components/SequenceTextArea.js.map +1 -1
- package/dist/components/TextTrack.d.ts +3 -3
- package/dist/components/TextTrack.js +4 -1
- package/dist/components/TextTrack.js.map +1 -1
- package/dist/components/Track.js +21 -8
- package/dist/components/Track.js.map +1 -1
- package/dist/components/dialogs/ExportSVGDialog.js +19 -3
- package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
- package/dist/components/header/GappynessSlider.d.ts +6 -0
- package/dist/components/header/GappynessSlider.js +19 -0
- package/dist/components/header/GappynessSlider.js.map +1 -0
- package/dist/components/header/Header.js +3 -1
- package/dist/components/header/Header.js.map +1 -1
- package/dist/components/header/HeaderMenu.js +30 -14
- package/dist/components/header/HeaderMenu.js.map +1 -1
- package/dist/components/minimap/MinimapSVG.js +4 -3
- package/dist/components/minimap/MinimapSVG.js.map +1 -1
- package/dist/components/msa/MSACanvasBlock.js +56 -42
- package/dist/components/msa/MSACanvasBlock.js.map +1 -1
- package/dist/components/msa/renderMSABlock.js +53 -10
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/tracks/renderTracksSvg.d.ts +29 -0
- package/dist/components/tracks/renderTracksSvg.js +83 -0
- package/dist/components/tracks/renderTracksSvg.js.map +1 -0
- package/dist/components/tree/TreeCanvasBlock.js +1 -1
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/TreeNodeMenu.js +2 -2
- package/dist/components/tree/TreeNodeMenu.js.map +1 -1
- package/dist/components/tree/renderTreeCanvas.js +1 -1
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/constants.d.ts +22 -0
- package/dist/constants.js +26 -0
- package/dist/constants.js.map +1 -0
- package/dist/layout.js.map +1 -1
- package/dist/model/msaModel.js +3 -2
- package/dist/model/msaModel.js.map +1 -1
- package/dist/model/treeModel.js +9 -8
- package/dist/model/treeModel.js.map +1 -1
- package/dist/model.d.ts +256 -15
- package/dist/model.js +408 -128
- package/dist/model.js.map +1 -1
- package/dist/neighborJoining.d.ts +1 -0
- package/dist/neighborJoining.js +839 -0
- package/dist/neighborJoining.js.map +1 -0
- package/dist/neighborJoining.test.d.ts +1 -0
- package/dist/neighborJoining.test.js +110 -0
- package/dist/neighborJoining.test.js.map +1 -0
- package/dist/parsers/A3mMSA.d.ts +43 -0
- package/dist/parsers/A3mMSA.js +277 -0
- package/dist/parsers/A3mMSA.js.map +1 -0
- package/dist/parsers/A3mMSA.test.d.ts +1 -0
- package/dist/parsers/A3mMSA.test.js +138 -0
- package/dist/parsers/A3mMSA.test.js.map +1 -0
- package/dist/parsers/ClustalMSA.d.ts +4 -4
- package/dist/parsers/ClustalMSA.js +3 -1
- package/dist/parsers/ClustalMSA.js.map +1 -1
- package/dist/parsers/FastaMSA.js +17 -16
- package/dist/parsers/FastaMSA.js.map +1 -1
- package/dist/renderToSvg.d.ts +1 -0
- package/dist/renderToSvg.js +48 -18
- package/dist/renderToSvg.js.map +1 -1
- package/dist/rowCoordinateCalculations.js +3 -5
- package/dist/rowCoordinateCalculations.js.map +1 -1
- package/dist/rowCoordinateCalculations.test.js +14 -2
- package/dist/rowCoordinateCalculations.test.js.map +1 -1
- package/dist/seqCoordToRowSpecificGlobalCoord.js +9 -5
- package/dist/seqCoordToRowSpecificGlobalCoord.js.map +1 -1
- package/dist/seqCoordToRowSpecificGlobalCoord.test.js +6 -6
- package/dist/types.d.ts +2 -3
- package/dist/util.js +17 -9
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +6 -6
- package/src/colorSchemes.ts +1 -179
- package/src/components/ConservationTrack.tsx +104 -0
- package/src/components/Loading.tsx +44 -2
- package/src/components/MSAView.tsx +68 -0
- package/src/components/SequenceTextArea.tsx +3 -2
- package/src/components/TextTrack.tsx +7 -4
- package/src/components/Track.tsx +25 -9
- package/src/components/dialogs/ExportSVGDialog.tsx +25 -1
- package/src/components/header/GappynessSlider.tsx +35 -0
- package/src/components/header/Header.tsx +3 -1
- package/src/components/header/HeaderMenu.tsx +36 -15
- package/src/components/minimap/MinimapSVG.tsx +6 -3
- package/src/components/msa/MSACanvasBlock.tsx +66 -48
- package/src/components/msa/renderMSABlock.ts +82 -22
- package/src/components/tracks/renderTracksSvg.ts +157 -0
- package/src/components/tree/TreeCanvasBlock.tsx +1 -1
- package/src/components/tree/TreeNodeMenu.tsx +2 -2
- package/src/components/tree/renderTreeCanvas.ts +1 -1
- package/src/constants.ts +27 -0
- package/src/layout.ts +1 -6
- package/src/model/msaModel.ts +4 -2
- package/src/model/treeModel.ts +19 -8
- package/src/model.ts +496 -140
- package/src/neighborJoining.test.ts +129 -0
- package/src/neighborJoining.ts +885 -0
- package/src/parsers/A3mMSA.test.ts +164 -0
- package/src/parsers/A3mMSA.ts +321 -0
- package/src/parsers/ClustalMSA.ts +7 -5
- package/src/parsers/FastaMSA.ts +17 -17
- package/src/renderToSvg.tsx +105 -26
- package/src/rowCoordinateCalculations.test.ts +15 -2
- package/src/rowCoordinateCalculations.ts +3 -5
- package/src/seqCoordToRowSpecificGlobalCoord.test.ts +6 -6
- package/src/seqCoordToRowSpecificGlobalCoord.ts +9 -4
- package/src/types.ts +2 -4
- package/src/util.ts +21 -8
- package/src/version.ts +1 -1
- package/dist/components/dialogs/TracklistDialog.d.ts +0 -7
- package/dist/components/dialogs/TracklistDialog.js +0 -23
- package/dist/components/dialogs/TracklistDialog.js.map +0 -1
- package/src/components/dialogs/TracklistDialog.tsx +0 -73
package/dist/model.js
CHANGED
|
@@ -11,7 +11,9 @@ import { addDisposer, cast, types } from 'mobx-state-tree';
|
|
|
11
11
|
import Stockholm from 'stockholm-js';
|
|
12
12
|
import { blocksX, blocksY } from './calculateBlocks';
|
|
13
13
|
import colorSchemes from './colorSchemes';
|
|
14
|
+
import ConservationTrack from './components/ConservationTrack';
|
|
14
15
|
import TextTrack from './components/TextTrack';
|
|
16
|
+
import { defaultAllowedGappyness, defaultBgColor, defaultColWidth, defaultColorSchemeName, defaultContrastLettering, defaultCurrentAlignment, defaultDrawLabels, defaultDrawMsaLetters, defaultDrawNodeBubbles, defaultDrawTree, defaultHeight, defaultHideGaps, defaultLabelsAlignRight, defaultRowHeight, defaultScrollX, defaultScrollY, defaultShowBranchLen, defaultShowDomains, defaultSubFeatureRows, defaultTreeAreaWidth, defaultTreeWidth, defaultTreeWidthMatchesArea, } from './constants';
|
|
15
17
|
import { flatToTree } from './flatToTree';
|
|
16
18
|
import palettes from './ggplotPalettes';
|
|
17
19
|
import { measureTextCanvas } from './measureTextCanvas';
|
|
@@ -19,8 +21,10 @@ import { DataModelF } from './model/DataModel';
|
|
|
19
21
|
import { DialogQueueSessionMixin } from './model/DialogQueue';
|
|
20
22
|
import { MSAModelF } from './model/msaModel';
|
|
21
23
|
import { TreeModelF } from './model/treeModel';
|
|
24
|
+
import { calculateNeighborJoiningTree } from './neighborJoining';
|
|
22
25
|
import { parseAsn1 } from './parseAsn1';
|
|
23
26
|
import parseNewick from './parseNewick';
|
|
27
|
+
import A3mMSA from './parsers/A3mMSA';
|
|
24
28
|
import ClustalMSA from './parsers/ClustalMSA';
|
|
25
29
|
import EmfMSA from './parsers/EmfMSA';
|
|
26
30
|
import FastaMSA from './parsers/FastaMSA';
|
|
@@ -28,9 +32,7 @@ import StockholmMSA from './parsers/StockholmMSA';
|
|
|
28
32
|
import { reparseTree } from './reparseTree';
|
|
29
33
|
import { mouseOverCoordToGapRemovedRowCoord, mouseOverCoordToGlobalCoord, } from './rowCoordinateCalculations';
|
|
30
34
|
import { seqCoordToRowSpecificGlobalCoord } from './seqCoordToRowSpecificGlobalCoord';
|
|
31
|
-
import { collapse, generateNodeIds,
|
|
32
|
-
const defaultRowHeight = 16;
|
|
33
|
-
const defaultColWidth = 12;
|
|
35
|
+
import { collapse, generateNodeIds, len, maxLength, setBrLength, skipBlanks, } from './util';
|
|
34
36
|
const showZoomStarKey = 'msa-showZoomStar';
|
|
35
37
|
/**
|
|
36
38
|
* #stateModel MsaView
|
|
@@ -50,23 +52,23 @@ function stateModelFactory() {
|
|
|
50
52
|
/**
|
|
51
53
|
* #property
|
|
52
54
|
*/
|
|
53
|
-
showDomains:
|
|
55
|
+
showDomains: defaultShowDomains,
|
|
54
56
|
/**
|
|
55
57
|
* #property
|
|
56
58
|
*/
|
|
57
|
-
hideGaps:
|
|
59
|
+
hideGaps: defaultHideGaps,
|
|
58
60
|
/**
|
|
59
61
|
* #property
|
|
60
62
|
*/
|
|
61
|
-
allowedGappyness:
|
|
63
|
+
allowedGappyness: defaultAllowedGappyness,
|
|
62
64
|
/**
|
|
63
65
|
* #property
|
|
64
66
|
*/
|
|
65
|
-
contrastLettering:
|
|
67
|
+
contrastLettering: defaultContrastLettering,
|
|
66
68
|
/**
|
|
67
69
|
* #property
|
|
68
70
|
*/
|
|
69
|
-
subFeatureRows:
|
|
71
|
+
subFeatureRows: defaultSubFeatureRows,
|
|
70
72
|
/**
|
|
71
73
|
* #property
|
|
72
74
|
* hardcoded view type
|
|
@@ -75,12 +77,12 @@ function stateModelFactory() {
|
|
|
75
77
|
/**
|
|
76
78
|
* #property
|
|
77
79
|
*/
|
|
78
|
-
drawMsaLetters:
|
|
80
|
+
drawMsaLetters: defaultDrawMsaLetters,
|
|
79
81
|
/**
|
|
80
82
|
* #property
|
|
81
83
|
* height of the div containing the view, px
|
|
82
84
|
*/
|
|
83
|
-
height: types.optional(types.number,
|
|
85
|
+
height: types.optional(types.number, defaultHeight),
|
|
84
86
|
/**
|
|
85
87
|
* #property
|
|
86
88
|
* height of each row, px
|
|
@@ -90,12 +92,12 @@ function stateModelFactory() {
|
|
|
90
92
|
* #property
|
|
91
93
|
* scroll position, Y-offset, px
|
|
92
94
|
*/
|
|
93
|
-
scrollY:
|
|
95
|
+
scrollY: defaultScrollY,
|
|
94
96
|
/**
|
|
95
97
|
* #property
|
|
96
98
|
* scroll position, X-offset, px
|
|
97
99
|
*/
|
|
98
|
-
scrollX:
|
|
100
|
+
scrollX: defaultScrollX,
|
|
99
101
|
/**
|
|
100
102
|
* #property
|
|
101
103
|
* width of columns, px
|
|
@@ -121,7 +123,7 @@ function stateModelFactory() {
|
|
|
121
123
|
* #property
|
|
122
124
|
*
|
|
123
125
|
*/
|
|
124
|
-
currentAlignment:
|
|
126
|
+
currentAlignment: defaultCurrentAlignment,
|
|
125
127
|
/**
|
|
126
128
|
* #property
|
|
127
129
|
* array of tree parent nodes that are 'collapsed' (all children are
|
|
@@ -289,7 +291,7 @@ function stateModelFactory() {
|
|
|
289
291
|
self.loadingMSA = arg;
|
|
290
292
|
},
|
|
291
293
|
/**
|
|
292
|
-
* #
|
|
294
|
+
* #action
|
|
293
295
|
*/
|
|
294
296
|
setShowZoomStar(arg) {
|
|
295
297
|
self.showZoomStar = arg;
|
|
@@ -338,7 +340,7 @@ function stateModelFactory() {
|
|
|
338
340
|
return;
|
|
339
341
|
}
|
|
340
342
|
// Find the node in the hierarchy
|
|
341
|
-
const node = self.hierarchy.find(
|
|
343
|
+
const node = self.hierarchy.find(n => n.data.id === nodeId);
|
|
342
344
|
if (!node) {
|
|
343
345
|
self.hoveredTreeNode = undefined;
|
|
344
346
|
return;
|
|
@@ -409,7 +411,7 @@ function stateModelFactory() {
|
|
|
409
411
|
/**
|
|
410
412
|
* #action
|
|
411
413
|
*/
|
|
412
|
-
|
|
414
|
+
toggleCollapsedLeaf(node) {
|
|
413
415
|
if (self.collapsedLeaves.includes(node)) {
|
|
414
416
|
self.collapsedLeaves.remove(node);
|
|
415
417
|
}
|
|
@@ -461,11 +463,21 @@ function stateModelFactory() {
|
|
|
461
463
|
},
|
|
462
464
|
}))
|
|
463
465
|
.views(self => ({
|
|
466
|
+
/**
|
|
467
|
+
* #getter
|
|
468
|
+
* hideGaps takes effect when there are collapsed rows or allowedGappyness < 100
|
|
469
|
+
*/
|
|
470
|
+
get hideGapsEffective() {
|
|
471
|
+
return (self.hideGaps &&
|
|
472
|
+
(self.collapsed.length > 0 ||
|
|
473
|
+
self.collapsedLeaves.length > 0 ||
|
|
474
|
+
self.allowedGappyness < 100));
|
|
475
|
+
},
|
|
464
476
|
/**
|
|
465
477
|
* #getter
|
|
466
478
|
*/
|
|
467
479
|
get realAllowedGappyness() {
|
|
468
|
-
return
|
|
480
|
+
return this.hideGapsEffective ? self.allowedGappyness : 100;
|
|
469
481
|
},
|
|
470
482
|
/**
|
|
471
483
|
* #getter
|
|
@@ -548,6 +560,9 @@ function stateModelFactory() {
|
|
|
548
560
|
if (Stockholm.sniff(text)) {
|
|
549
561
|
return new StockholmMSA(text, self.currentAlignment);
|
|
550
562
|
}
|
|
563
|
+
else if (A3mMSA.sniff(text)) {
|
|
564
|
+
return new A3mMSA(text);
|
|
565
|
+
}
|
|
551
566
|
else if (text.startsWith('>')) {
|
|
552
567
|
return new FastaMSA(text);
|
|
553
568
|
}
|
|
@@ -595,6 +610,33 @@ function stateModelFactory() {
|
|
|
595
610
|
const { mouseRow } = self;
|
|
596
611
|
return mouseRow === undefined ? undefined : this.rowNames[mouseRow];
|
|
597
612
|
},
|
|
613
|
+
/**
|
|
614
|
+
* #getter
|
|
615
|
+
* Returns insertion info if mouse is hovering over an insertion indicator
|
|
616
|
+
*/
|
|
617
|
+
get hoveredInsertion() {
|
|
618
|
+
const { mouseCol, mouseRow } = self;
|
|
619
|
+
if (mouseCol === undefined || mouseRow === undefined) {
|
|
620
|
+
return undefined;
|
|
621
|
+
}
|
|
622
|
+
const rowName = this.rowNames[mouseRow];
|
|
623
|
+
if (!rowName) {
|
|
624
|
+
return undefined;
|
|
625
|
+
}
|
|
626
|
+
const insertions = this.insertionPositions.get(rowName);
|
|
627
|
+
if (!insertions) {
|
|
628
|
+
return undefined;
|
|
629
|
+
}
|
|
630
|
+
const insertion = insertions.find(ins => ins.pos === mouseCol);
|
|
631
|
+
if (insertion) {
|
|
632
|
+
return {
|
|
633
|
+
rowName,
|
|
634
|
+
col: mouseCol,
|
|
635
|
+
letters: insertion.letters,
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
return undefined;
|
|
639
|
+
},
|
|
598
640
|
/**
|
|
599
641
|
* #getter
|
|
600
642
|
*/
|
|
@@ -615,7 +657,7 @@ function stateModelFactory() {
|
|
|
615
657
|
[...self.collapsed, ...self.collapsedLeaves]
|
|
616
658
|
.map(collapsedId => hier.find(node => node.data.id === collapsedId))
|
|
617
659
|
.filter(notEmpty)
|
|
618
|
-
.
|
|
660
|
+
.forEach(node => {
|
|
619
661
|
collapse(node);
|
|
620
662
|
});
|
|
621
663
|
return hier;
|
|
@@ -637,28 +679,35 @@ function stateModelFactory() {
|
|
|
637
679
|
* #getter
|
|
638
680
|
*/
|
|
639
681
|
get blanks() {
|
|
640
|
-
const {
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
682
|
+
const { hideGapsEffective, realAllowedGappyness } = self;
|
|
683
|
+
if (!hideGapsEffective) {
|
|
684
|
+
return [];
|
|
685
|
+
}
|
|
686
|
+
const strs = this.leaves
|
|
687
|
+
.map(leaf => this.MSA?.getRow(leaf.data.name))
|
|
688
|
+
.filter(notEmpty);
|
|
689
|
+
if (strs.length === 0) {
|
|
690
|
+
return [];
|
|
691
|
+
}
|
|
692
|
+
const numCols = strs[0].length;
|
|
693
|
+
const numRows = strs.length;
|
|
694
|
+
const threshold = Math.ceil((realAllowedGappyness / 100) * numRows);
|
|
695
|
+
const blankCounts = new Uint16Array(numCols);
|
|
696
|
+
for (let j = 0; j < numRows; j++) {
|
|
697
|
+
const str = strs[j];
|
|
698
|
+
for (let i = 0; i < numCols; i++) {
|
|
699
|
+
// bit trick: (code - 45) >>> 0 <= 1 checks for '-' (45) or '.' (46)
|
|
700
|
+
if ((str.charCodeAt(i) - 45) >>> 0 <= 1) {
|
|
701
|
+
blankCounts[i]++;
|
|
659
702
|
}
|
|
660
703
|
}
|
|
661
704
|
}
|
|
705
|
+
const blanks = [];
|
|
706
|
+
for (let i = 0; i < numCols; i++) {
|
|
707
|
+
if (blankCounts[i] >= threshold) {
|
|
708
|
+
blanks.push(i);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
662
711
|
return blanks;
|
|
663
712
|
},
|
|
664
713
|
/**
|
|
@@ -667,6 +716,62 @@ function stateModelFactory() {
|
|
|
667
716
|
get blanksSet() {
|
|
668
717
|
return new Set(this.blanks);
|
|
669
718
|
},
|
|
719
|
+
/**
|
|
720
|
+
* #getter
|
|
721
|
+
* Returns a map of row name to array of insertions with display position and letters
|
|
722
|
+
*/
|
|
723
|
+
get insertionPositions() {
|
|
724
|
+
const { hideGapsEffective } = self;
|
|
725
|
+
const { blanks, rows } = this;
|
|
726
|
+
const blanksLen = blanks.length;
|
|
727
|
+
if (blanksLen === 0 || !hideGapsEffective) {
|
|
728
|
+
return new Map();
|
|
729
|
+
}
|
|
730
|
+
const result = new Map();
|
|
731
|
+
for (const [name, seq] of rows) {
|
|
732
|
+
const insertions = [];
|
|
733
|
+
let displayPos = 0;
|
|
734
|
+
let blankIdx = 0;
|
|
735
|
+
let currentInsertPos = -1;
|
|
736
|
+
let letterChars = [];
|
|
737
|
+
const seqLen = seq.length;
|
|
738
|
+
for (let i = 0; i < seqLen; i++) {
|
|
739
|
+
if (blankIdx < blanksLen && blanks[blankIdx] === i) {
|
|
740
|
+
// bit trick: (code - 45) >>> 0 <= 1 checks for '-' (45) or '.' (46)
|
|
741
|
+
const code = seq.charCodeAt(i);
|
|
742
|
+
if (!((code - 45) >>> 0 <= 1)) {
|
|
743
|
+
if (currentInsertPos === displayPos) {
|
|
744
|
+
letterChars.push(seq[i]);
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
if (letterChars.length > 0) {
|
|
748
|
+
insertions.push({
|
|
749
|
+
pos: currentInsertPos,
|
|
750
|
+
letters: letterChars.join(''),
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
currentInsertPos = displayPos;
|
|
754
|
+
letterChars = [seq[i]];
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
blankIdx++;
|
|
758
|
+
}
|
|
759
|
+
else {
|
|
760
|
+
displayPos++;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
if (letterChars.length > 0) {
|
|
764
|
+
insertions.push({
|
|
765
|
+
pos: currentInsertPos,
|
|
766
|
+
letters: letterChars.join(''),
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
if (insertions.length > 0) {
|
|
770
|
+
result.set(name, insertions);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return result;
|
|
774
|
+
},
|
|
670
775
|
/**
|
|
671
776
|
* #getter
|
|
672
777
|
*/
|
|
@@ -698,10 +803,10 @@ function stateModelFactory() {
|
|
|
698
803
|
* #getter
|
|
699
804
|
*/
|
|
700
805
|
get columns2d() {
|
|
701
|
-
const {
|
|
806
|
+
const { hideGapsEffective } = self;
|
|
702
807
|
return this.rows
|
|
703
808
|
.map(r => r[1])
|
|
704
|
-
.map(str => (
|
|
809
|
+
.map(str => (hideGapsEffective ? skipBlanks(this.blanks, str) : str));
|
|
705
810
|
},
|
|
706
811
|
/**
|
|
707
812
|
* #getter
|
|
@@ -734,6 +839,182 @@ function stateModelFactory() {
|
|
|
734
839
|
get colStatsSums() {
|
|
735
840
|
return this.colStats.map(val => sum(Object.values(val)));
|
|
736
841
|
},
|
|
842
|
+
/**
|
|
843
|
+
* #getter
|
|
844
|
+
* Pre-computed consensus letter and percent identity color per column.
|
|
845
|
+
* Used by percent_identity_dynamic color scheme.
|
|
846
|
+
*/
|
|
847
|
+
get colConsensus() {
|
|
848
|
+
const { colStats, colStatsSums } = this;
|
|
849
|
+
return colStats.map((stats, i) => {
|
|
850
|
+
const total = colStatsSums[i];
|
|
851
|
+
let maxCount = 0;
|
|
852
|
+
let letter = '';
|
|
853
|
+
for (const key in stats) {
|
|
854
|
+
const val = stats[key];
|
|
855
|
+
if (val > maxCount && key !== '-' && key !== '.') {
|
|
856
|
+
maxCount = val;
|
|
857
|
+
letter = key;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
const proportion = maxCount / total;
|
|
861
|
+
return {
|
|
862
|
+
letter,
|
|
863
|
+
color: proportion > 0.4
|
|
864
|
+
? `hsl(240, 30%, ${100 * Math.max(1 - proportion / 3, 0.3)}%)`
|
|
865
|
+
: undefined,
|
|
866
|
+
};
|
|
867
|
+
});
|
|
868
|
+
},
|
|
869
|
+
/**
|
|
870
|
+
* #getter
|
|
871
|
+
* Pre-computed ClustalX colors per column.
|
|
872
|
+
* Returns a map of letter -> color for each column.
|
|
873
|
+
* ref http://www.jalview.org/help/html/colourSchemes/clustal.html
|
|
874
|
+
*/
|
|
875
|
+
get colClustalX() {
|
|
876
|
+
const { colStats, colStatsSums } = this;
|
|
877
|
+
return colStats.map((stats, i) => {
|
|
878
|
+
const total = colStatsSums[i];
|
|
879
|
+
const colors = {};
|
|
880
|
+
const W = stats.W ?? 0;
|
|
881
|
+
const L = stats.L ?? 0;
|
|
882
|
+
const V = stats.V ?? 0;
|
|
883
|
+
const I = stats.I ?? 0;
|
|
884
|
+
const M = stats.M ?? 0;
|
|
885
|
+
const A = stats.A ?? 0;
|
|
886
|
+
const F = stats.F ?? 0;
|
|
887
|
+
const C = stats.C ?? 0;
|
|
888
|
+
const H = stats.H ?? 0;
|
|
889
|
+
const P = stats.P ?? 0;
|
|
890
|
+
const R = stats.R ?? 0;
|
|
891
|
+
const K = stats.K ?? 0;
|
|
892
|
+
const Q = stats.Q ?? 0;
|
|
893
|
+
const E = stats.E ?? 0;
|
|
894
|
+
const D = stats.D ?? 0;
|
|
895
|
+
const T = stats.T ?? 0;
|
|
896
|
+
const S = stats.S ?? 0;
|
|
897
|
+
const G = stats.G ?? 0;
|
|
898
|
+
const Y = stats.Y ?? 0;
|
|
899
|
+
const N = stats.N ?? 0;
|
|
900
|
+
const WLVIMAFCHPY = W + L + V + I + M + A + F + C + H + P + Y;
|
|
901
|
+
const KR = K + R;
|
|
902
|
+
const QE = Q + E;
|
|
903
|
+
const ED = E + D;
|
|
904
|
+
const TS = T + S;
|
|
905
|
+
if (WLVIMAFCHPY / total > 0.6) {
|
|
906
|
+
colors.W = 'rgb(128,179,230)';
|
|
907
|
+
colors.L = 'rgb(128,179,230)';
|
|
908
|
+
colors.V = 'rgb(128,179,230)';
|
|
909
|
+
colors.A = 'rgb(128,179,230)';
|
|
910
|
+
colors.I = 'rgb(128,179,230)';
|
|
911
|
+
colors.M = 'rgb(128,179,230)';
|
|
912
|
+
colors.F = 'rgb(128,179,230)';
|
|
913
|
+
colors.C = 'rgb(128,179,230)';
|
|
914
|
+
}
|
|
915
|
+
if (KR / total > 0.6 ||
|
|
916
|
+
K / total > 0.8 ||
|
|
917
|
+
R / total > 0.8 ||
|
|
918
|
+
Q / total > 0.8) {
|
|
919
|
+
colors.K = '#d88';
|
|
920
|
+
colors.R = '#d88';
|
|
921
|
+
}
|
|
922
|
+
if (KR / total > 0.6 ||
|
|
923
|
+
QE / total > 0.5 ||
|
|
924
|
+
E / total > 0.8 ||
|
|
925
|
+
Q / total > 0.8 ||
|
|
926
|
+
D / total > 0.8) {
|
|
927
|
+
colors.E = 'rgb(192, 72, 192)';
|
|
928
|
+
}
|
|
929
|
+
if (KR / total > 0.6 ||
|
|
930
|
+
ED / total > 0.5 ||
|
|
931
|
+
K / total > 0.8 ||
|
|
932
|
+
R / total > 0.8 ||
|
|
933
|
+
Q / total > 0.8) {
|
|
934
|
+
colors.D = 'rgb(204, 77, 204)';
|
|
935
|
+
}
|
|
936
|
+
if (N / total > 0.5 || Y / total > 0.85) {
|
|
937
|
+
colors.N = '#8f8';
|
|
938
|
+
}
|
|
939
|
+
if (KR / total > 0.6 ||
|
|
940
|
+
QE / total > 0.6 ||
|
|
941
|
+
Q / total > 0.85 ||
|
|
942
|
+
E / total > 0.85 ||
|
|
943
|
+
K / total > 0.85 ||
|
|
944
|
+
R / total > 0.85) {
|
|
945
|
+
colors.Q = '#8f8';
|
|
946
|
+
}
|
|
947
|
+
if (WLVIMAFCHPY / total > 0.6 ||
|
|
948
|
+
TS / total > 0.5 ||
|
|
949
|
+
S / total > 0.85 ||
|
|
950
|
+
T / total > 0.85) {
|
|
951
|
+
colors.S = 'rgb(26,204,26)';
|
|
952
|
+
colors.T = 'rgb(26,204,26)';
|
|
953
|
+
}
|
|
954
|
+
if (C / total > 0.85) {
|
|
955
|
+
colors.C = 'rgb(240, 128, 128)';
|
|
956
|
+
}
|
|
957
|
+
if (G / total > 0) {
|
|
958
|
+
colors.G = 'rgb(240, 144, 72)';
|
|
959
|
+
}
|
|
960
|
+
if (P / total > 0) {
|
|
961
|
+
colors.P = 'rgb(204, 204, 0)';
|
|
962
|
+
}
|
|
963
|
+
if (WLVIMAFCHPY / total > 0.6 ||
|
|
964
|
+
W / total > 0.85 ||
|
|
965
|
+
Y / total > 0.85 ||
|
|
966
|
+
A / total > 0.85 ||
|
|
967
|
+
C / total > 0.85 ||
|
|
968
|
+
P / total > 0.85 ||
|
|
969
|
+
Q / total > 0.85 ||
|
|
970
|
+
F / total > 0.85 ||
|
|
971
|
+
H / total > 0.85 ||
|
|
972
|
+
I / total > 0.85 ||
|
|
973
|
+
L / total > 0.85 ||
|
|
974
|
+
M / total > 0.85 ||
|
|
975
|
+
V / total > 0.85) {
|
|
976
|
+
colors.H = 'rgb(26, 179, 179)';
|
|
977
|
+
colors.Y = 'rgb(26, 179, 179)';
|
|
978
|
+
}
|
|
979
|
+
return colors;
|
|
980
|
+
});
|
|
981
|
+
},
|
|
982
|
+
/**
|
|
983
|
+
* #getter
|
|
984
|
+
* Conservation score per column using Shannon entropy (biojs-msa style).
|
|
985
|
+
* Conservation = (1 - H/Hmax) * (1 - gapFraction)
|
|
986
|
+
* Returns values 0-1 where 1 = fully conserved, 0 = no conservation.
|
|
987
|
+
*/
|
|
988
|
+
get conservation() {
|
|
989
|
+
const { colStats, colStatsSums } = this;
|
|
990
|
+
const alphabetSize = 20;
|
|
991
|
+
const maxEntropy = Math.log2(alphabetSize);
|
|
992
|
+
return colStats.map((stats, i) => {
|
|
993
|
+
const total = colStatsSums[i];
|
|
994
|
+
if (!total) {
|
|
995
|
+
return 0;
|
|
996
|
+
}
|
|
997
|
+
const gapCount = (stats['-'] || 0) + (stats['.'] || 0);
|
|
998
|
+
const nonGapTotal = total - gapCount;
|
|
999
|
+
if (nonGapTotal === 0) {
|
|
1000
|
+
return 0;
|
|
1001
|
+
}
|
|
1002
|
+
let entropy = 0;
|
|
1003
|
+
for (const letter of Object.keys(stats)) {
|
|
1004
|
+
if (letter === '-' || letter === '.') {
|
|
1005
|
+
continue;
|
|
1006
|
+
}
|
|
1007
|
+
const count = stats[letter];
|
|
1008
|
+
const freq = count / nonGapTotal;
|
|
1009
|
+
if (freq > 0) {
|
|
1010
|
+
entropy -= freq * Math.log2(freq);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
const gapFraction = gapCount / total;
|
|
1014
|
+
const conservation = Math.max(0, 1 - entropy / maxEntropy);
|
|
1015
|
+
return conservation * (1 - gapFraction);
|
|
1016
|
+
});
|
|
1017
|
+
},
|
|
737
1018
|
/**
|
|
738
1019
|
* #getter
|
|
739
1020
|
* generates a new tree that is clustered with x,y positions
|
|
@@ -851,6 +1132,17 @@ function stateModelFactory() {
|
|
|
851
1132
|
setDrawMsaLetters(arg) {
|
|
852
1133
|
self.drawMsaLetters = arg;
|
|
853
1134
|
},
|
|
1135
|
+
/**
|
|
1136
|
+
* #action
|
|
1137
|
+
* Calculate a neighbor joining tree from the current MSA using BLOSUM62 distances
|
|
1138
|
+
*/
|
|
1139
|
+
calculateNeighborJoiningTreeFromMSA() {
|
|
1140
|
+
if (self.rows.length < 2) {
|
|
1141
|
+
throw new Error('Need at least 2 sequences to build a tree');
|
|
1142
|
+
}
|
|
1143
|
+
const newickTree = calculateNeighborJoiningTree(self.rows);
|
|
1144
|
+
self.setTree(newickTree);
|
|
1145
|
+
},
|
|
854
1146
|
/**
|
|
855
1147
|
* #action
|
|
856
1148
|
*/
|
|
@@ -972,33 +1264,17 @@ function stateModelFactory() {
|
|
|
972
1264
|
get seqConsensus() {
|
|
973
1265
|
return self.MSA?.seqConsensus;
|
|
974
1266
|
},
|
|
975
|
-
/**
|
|
976
|
-
* #getter
|
|
977
|
-
*/
|
|
978
|
-
get conservation() {
|
|
979
|
-
if (self.columns2d.length) {
|
|
980
|
-
for (let i = 0; i < self.columns2d[0].length; i++) {
|
|
981
|
-
const col = [];
|
|
982
|
-
for (const column of self.columns2d) {
|
|
983
|
-
col.push(column[i]);
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
return ['a'];
|
|
988
|
-
},
|
|
989
1267
|
/**
|
|
990
1268
|
* #getter
|
|
991
1269
|
*/
|
|
992
1270
|
get adapterTrackModels() {
|
|
993
|
-
const { rowHeight, MSA,
|
|
994
|
-
return (MSA?.tracks
|
|
1271
|
+
const { rowHeight, MSA, hideGapsEffective, blanks } = self;
|
|
1272
|
+
return (MSA?.tracks
|
|
1273
|
+
.filter(t => t.data)
|
|
1274
|
+
.map(t => ({
|
|
995
1275
|
model: {
|
|
996
1276
|
...t,
|
|
997
|
-
data: t.data
|
|
998
|
-
? hideGaps
|
|
999
|
-
? skipBlanks(blanks, t.data)
|
|
1000
|
-
: t.data
|
|
1001
|
-
: undefined,
|
|
1277
|
+
data: hideGapsEffective ? skipBlanks(blanks, t.data) : t.data,
|
|
1002
1278
|
height: rowHeight,
|
|
1003
1279
|
},
|
|
1004
1280
|
ReactComponent: TextTrack,
|
|
@@ -1008,7 +1284,15 @@ function stateModelFactory() {
|
|
|
1008
1284
|
* #getter
|
|
1009
1285
|
*/
|
|
1010
1286
|
get tracks() {
|
|
1011
|
-
|
|
1287
|
+
const conservationTrack = {
|
|
1288
|
+
model: {
|
|
1289
|
+
id: 'conservation',
|
|
1290
|
+
name: 'Conservation',
|
|
1291
|
+
height: 40,
|
|
1292
|
+
},
|
|
1293
|
+
ReactComponent: ConservationTrack,
|
|
1294
|
+
};
|
|
1295
|
+
return [...this.adapterTrackModels, conservationTrack];
|
|
1012
1296
|
},
|
|
1013
1297
|
/**
|
|
1014
1298
|
* #getter
|
|
@@ -1359,81 +1643,77 @@ function stateModelFactory() {
|
|
|
1359
1643
|
}))
|
|
1360
1644
|
.postProcessSnapshot(result => {
|
|
1361
1645
|
const snap = result;
|
|
1362
|
-
const { data: { tree, msa, treeMetadata },
|
|
1363
|
-
//
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
drawMsaLetters: true,
|
|
1372
|
-
height: 550,
|
|
1373
|
-
rowHeight: defaultRowHeight,
|
|
1374
|
-
scrollY: 0,
|
|
1375
|
-
scrollX: 0,
|
|
1376
|
-
colWidth: defaultColWidth,
|
|
1377
|
-
currentAlignment: 0,
|
|
1378
|
-
// MSA model defaults
|
|
1379
|
-
bgColor: true,
|
|
1380
|
-
colorSchemeName: 'maeditor',
|
|
1381
|
-
// Tree model defaults
|
|
1382
|
-
drawLabels: true,
|
|
1383
|
-
labelsAlignRight: false,
|
|
1384
|
-
treeAreaWidth: 400,
|
|
1385
|
-
treeWidth: 300,
|
|
1386
|
-
treeWidthMatchesArea: true,
|
|
1387
|
-
showBranchLen: true,
|
|
1388
|
-
drawTree: true,
|
|
1389
|
-
drawNodeBubbles: true,
|
|
1390
|
-
};
|
|
1391
|
-
// Properties that should always be included even if they match defaults
|
|
1392
|
-
const alwaysInclude = new Set(['id', 'type', 'relativeTo']);
|
|
1393
|
-
// Filter out properties that match default values
|
|
1394
|
-
function filterDefaults(obj) {
|
|
1395
|
-
const filtered = {};
|
|
1396
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
1397
|
-
// Always include essential properties
|
|
1398
|
-
if (alwaysInclude.has(key)) {
|
|
1399
|
-
filtered[key] = value;
|
|
1400
|
-
continue;
|
|
1401
|
-
}
|
|
1402
|
-
// Skip if value matches default
|
|
1403
|
-
if (defaults[key] === value) {
|
|
1404
|
-
continue;
|
|
1405
|
-
}
|
|
1406
|
-
// Handle nested objects
|
|
1407
|
-
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
1408
|
-
const filteredNested = filterDefaults(value);
|
|
1409
|
-
// Only include nested object if it has non-default properties
|
|
1410
|
-
if (Object.keys(filteredNested).length > 0) {
|
|
1411
|
-
filtered[key] = filteredNested;
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
else if (Array.isArray(value)) {
|
|
1415
|
-
// Only include arrays that aren't empty
|
|
1416
|
-
if (value.length > 0) {
|
|
1417
|
-
filtered[key] = value;
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1420
|
-
else {
|
|
1421
|
-
// Include non-default primitives
|
|
1422
|
-
filtered[key] = value;
|
|
1423
|
-
}
|
|
1424
|
-
}
|
|
1425
|
-
return filtered;
|
|
1426
|
-
}
|
|
1427
|
-
const filteredRest = filterDefaults(rest);
|
|
1646
|
+
const { data: { tree, msa, treeMetadata },
|
|
1647
|
+
// Main model properties
|
|
1648
|
+
showDomains, hideGaps, allowedGappyness, contrastLettering, subFeatureRows, drawMsaLetters, height, rowHeight, scrollY, scrollX, colWidth, currentAlignment, collapsed, collapsedLeaves, showOnly, turnedOffTracks, featureFilters, relativeTo,
|
|
1649
|
+
// MSA model properties
|
|
1650
|
+
bgColor, colorSchemeName,
|
|
1651
|
+
// Tree model properties
|
|
1652
|
+
drawLabels, labelsAlignRight, treeAreaWidth, treeWidth, treeWidthMatchesArea, showBranchLen, drawTree, drawNodeBubbles,
|
|
1653
|
+
// Always include
|
|
1654
|
+
...rest } = snap;
|
|
1428
1655
|
// remove the MSA/tree data from the tree if the filehandle available in
|
|
1429
1656
|
// which case it can be reloaded on refresh
|
|
1430
1657
|
return {
|
|
1658
|
+
...rest,
|
|
1431
1659
|
data: {
|
|
1432
1660
|
...(result.treeFilehandle ? {} : { tree }),
|
|
1433
1661
|
...(result.msaFilehandle ? {} : { msa }),
|
|
1434
1662
|
...(result.treeMetadataFilehandle ? {} : { treeMetadata }),
|
|
1435
1663
|
},
|
|
1436
|
-
|
|
1664
|
+
// Main model - only include non-default values
|
|
1665
|
+
...(showDomains !== defaultShowDomains ? { showDomains } : {}),
|
|
1666
|
+
...(hideGaps !== defaultHideGaps ? { hideGaps } : {}),
|
|
1667
|
+
...(allowedGappyness !== defaultAllowedGappyness
|
|
1668
|
+
? { allowedGappyness }
|
|
1669
|
+
: {}),
|
|
1670
|
+
...(contrastLettering !== defaultContrastLettering
|
|
1671
|
+
? { contrastLettering }
|
|
1672
|
+
: {}),
|
|
1673
|
+
...(subFeatureRows !== defaultSubFeatureRows ? { subFeatureRows } : {}),
|
|
1674
|
+
...(drawMsaLetters !== defaultDrawMsaLetters ? { drawMsaLetters } : {}),
|
|
1675
|
+
...(height !== defaultHeight ? { height } : {}),
|
|
1676
|
+
...(rowHeight !== defaultRowHeight ? { rowHeight } : {}),
|
|
1677
|
+
...(scrollY !== defaultScrollY ? { scrollY } : {}),
|
|
1678
|
+
...(scrollX !== defaultScrollX ? { scrollX } : {}),
|
|
1679
|
+
...(colWidth !== defaultColWidth ? { colWidth } : {}),
|
|
1680
|
+
...(currentAlignment !== defaultCurrentAlignment
|
|
1681
|
+
? { currentAlignment }
|
|
1682
|
+
: {}),
|
|
1683
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
1684
|
+
...(collapsed?.length ? { collapsed } : {}),
|
|
1685
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
1686
|
+
...(collapsedLeaves?.length ? { collapsedLeaves } : {}),
|
|
1687
|
+
...(showOnly !== undefined ? { showOnly } : {}),
|
|
1688
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
1689
|
+
...(turnedOffTracks && Object.keys(turnedOffTracks).length > 0
|
|
1690
|
+
? { turnedOffTracks }
|
|
1691
|
+
: {}),
|
|
1692
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
1693
|
+
...(featureFilters && Object.keys(featureFilters).length > 0
|
|
1694
|
+
? { featureFilters }
|
|
1695
|
+
: {}),
|
|
1696
|
+
...(relativeTo !== undefined ? { relativeTo } : {}),
|
|
1697
|
+
// MSA model - only include non-default values
|
|
1698
|
+
...(bgColor !== defaultBgColor ? { bgColor } : {}),
|
|
1699
|
+
...(colorSchemeName !== defaultColorSchemeName
|
|
1700
|
+
? { colorSchemeName }
|
|
1701
|
+
: {}),
|
|
1702
|
+
// Tree model - only include non-default values
|
|
1703
|
+
...(drawLabels !== defaultDrawLabels ? { drawLabels } : {}),
|
|
1704
|
+
...(labelsAlignRight !== defaultLabelsAlignRight
|
|
1705
|
+
? { labelsAlignRight }
|
|
1706
|
+
: {}),
|
|
1707
|
+
...(treeAreaWidth !== defaultTreeAreaWidth ? { treeAreaWidth } : {}),
|
|
1708
|
+
...(treeWidth !== defaultTreeWidth ? { treeWidth } : {}),
|
|
1709
|
+
...(treeWidthMatchesArea !== defaultTreeWidthMatchesArea
|
|
1710
|
+
? { treeWidthMatchesArea }
|
|
1711
|
+
: {}),
|
|
1712
|
+
...(showBranchLen !== defaultShowBranchLen ? { showBranchLen } : {}),
|
|
1713
|
+
...(drawTree !== defaultDrawTree ? { drawTree } : {}),
|
|
1714
|
+
...(drawNodeBubbles !== defaultDrawNodeBubbles
|
|
1715
|
+
? { drawNodeBubbles }
|
|
1716
|
+
: {}),
|
|
1437
1717
|
};
|
|
1438
1718
|
});
|
|
1439
1719
|
}
|