@datagrok/bio 2.11.42 → 2.12.1
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/CHANGELOG.md +14 -0
- package/README.md +1 -1
- package/detectors.js +11 -11
- package/dist/36.js +1 -1
- package/dist/36.js.map +1 -1
- package/dist/413.js +1 -1
- package/dist/413.js.map +1 -1
- package/dist/590.js +1 -1
- package/dist/590.js.map +1 -1
- package/dist/709.js +1 -1
- package/dist/709.js.map +1 -1
- package/dist/895.js +1 -1
- package/dist/895.js.map +1 -1
- package/dist/package-test.js +3 -3
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +2 -2
- package/dist/package.js.map +1 -1
- package/files/tests/libraries/HELMmonomerSchema.json +1 -1
- package/package.json +11 -11
- package/src/analysis/sequence-activity-cliffs.ts +9 -9
- package/src/analysis/sequence-diversity-viewer.ts +3 -3
- package/src/analysis/sequence-search-base-viewer.ts +2 -2
- package/src/analysis/sequence-similarity-viewer.ts +10 -10
- package/src/analysis/sequence-space.ts +26 -23
- package/src/calculations/monomerLevelMols.ts +13 -11
- package/src/package.ts +12 -15
- package/src/tests/WebLogo-layout-tests.ts +5 -2
- package/src/tests/WebLogo-positions-test.ts +5 -5
- package/src/tests/bio-tests.ts +13 -6
- package/src/tests/converters-test.ts +4 -4
- package/src/tests/detectors-benchmark-tests.ts +5 -5
- package/src/tests/detectors-tests.ts +13 -13
- package/src/tests/fasta-export-tests.ts +10 -4
- package/src/tests/mm-distance-tests.ts +10 -10
- package/src/tests/msa-tests.ts +8 -15
- package/src/tests/renderers-monomer-placer.ts +3 -3
- package/src/tests/renderers-test.ts +6 -8
- package/src/tests/splitters-test.ts +14 -13
- package/src/tests/substructure-filters-tests.ts +143 -1
- package/src/tests/to-atomic-level-tests.ts +2 -2
- package/src/tests/units-handler-get-region.ts +4 -4
- package/src/tests/units-handler-splitted-tests.ts +19 -17
- package/src/tests/units-handler-tests.ts +32 -32
- package/src/utils/cell-renderer.ts +40 -34
- package/src/utils/check-input-column.ts +5 -5
- package/src/utils/context-menu.ts +9 -6
- package/src/utils/convert.ts +9 -9
- package/src/utils/get-region-func-editor.ts +11 -11
- package/src/utils/get-region.ts +10 -12
- package/src/utils/macromolecule-column-widget.ts +4 -3
- package/src/utils/monomer-lib/library-file-manager/event-manager.ts +1 -1
- package/src/utils/multiple-sequence-alignment-ui.ts +6 -6
- package/src/utils/pepsea.ts +1 -0
- package/src/utils/poly-tool/transformation.ts +3 -3
- package/src/utils/poly-tool/ui.ts +46 -135
- package/src/utils/save-as-fasta.ts +14 -15
- package/src/utils/sequence-to-mol.ts +4 -4
- package/src/viewers/web-logo-viewer.ts +46 -54
- package/src/widgets/bio-substructure-filter-types.ts +19 -45
- package/src/widgets/bio-substructure-filter.ts +45 -23
- package/src/widgets/composition-analysis-widget.ts +8 -8
|
@@ -5,144 +5,55 @@ import * as DG from 'datagrok-api/dg';
|
|
|
5
5
|
|
|
6
6
|
import {addTransformedColumn} from './transformation';
|
|
7
7
|
import {RULES_PATH, RULES_STORAGE_NAME} from './transformation';
|
|
8
|
+
import {ActiveFiles} from '@datagrok-libraries/utils/src/settings/active-files-base';
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async function getAllAvailableRuleFiles(): Promise<string[]> {
|
|
15
|
-
const list = await grok.dapi.files.list(RULES_PATH);
|
|
16
|
-
const paths = list.map((fileInfo) => {
|
|
17
|
-
return fileInfo.fullPath;
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
return paths;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function getUserRulesSettings(): Promise<UserRuleSettings> {
|
|
24
|
-
const resStr: string = await grok.dapi.userDataStorage.getValue(RULES_STORAGE_NAME, 'Settings', true);
|
|
25
|
-
const res = resStr ? JSON.parse(resStr) : {included: [], enotIncludedxplicit: []};
|
|
26
|
-
|
|
27
|
-
res.included = res.included instanceof Array ? res.included : [];
|
|
28
|
-
res.notIncluded = res.notIncluded instanceof Array ? res.notIncluded : [];
|
|
29
|
-
|
|
30
|
-
return res!;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async function setUserLibSettings(value: UserRuleSettings): Promise<void> {
|
|
34
|
-
await grok.dapi.userDataStorage.postValue(RULES_STORAGE_NAME, 'Settings', JSON.stringify(value), true);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export class PolyTool {
|
|
38
|
-
ruleFiles: string[];
|
|
39
|
-
userRuleSettings: UserRuleSettings;
|
|
40
|
-
ruleFilesInputs: HTMLDivElement;// DG.InputBase<boolean | null>[];
|
|
41
|
-
dialog: DG.Dialog;
|
|
42
|
-
|
|
43
|
-
constructor() {
|
|
44
|
-
this.ruleFiles = [];
|
|
45
|
-
this.userRuleSettings = {included: [], notIncluded: []};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
private updateRulesSelectionStatus(ruleFileName: string, isSelected: boolean): void {
|
|
49
|
-
const isRuleFileSelected = this.userRuleSettings.included.includes(ruleFileName);
|
|
50
|
-
|
|
51
|
-
if (!isRuleFileSelected && isSelected) {
|
|
52
|
-
this.userRuleSettings.included.push(ruleFileName);
|
|
53
|
-
this.userRuleSettings.included = this.userRuleSettings.included.sort();
|
|
54
|
-
|
|
55
|
-
const index = this.userRuleSettings.notIncluded.indexOf(ruleFileName);
|
|
56
|
-
if (index > -1)
|
|
57
|
-
this.userRuleSettings.notIncluded.splice(index, 1);
|
|
58
|
-
} else {
|
|
59
|
-
const index = this.userRuleSettings.included.indexOf(ruleFileName);
|
|
60
|
-
if (index > -1)
|
|
61
|
-
this.userRuleSettings.included.splice(index, 1);
|
|
62
|
-
|
|
63
|
-
this.userRuleSettings.notIncluded.push(ruleFileName);
|
|
64
|
-
this.userRuleSettings.notIncluded = this.userRuleSettings.notIncluded.sort();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
setUserLibSettings(this.userRuleSettings);
|
|
10
|
+
class RuleInputs extends ActiveFiles {
|
|
11
|
+
constructor(path: string, userStorageName: string, ext: string ) {
|
|
12
|
+
super(path, userStorageName, ext);
|
|
68
13
|
}
|
|
14
|
+
}
|
|
69
15
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
16
|
+
export async function getPolyToolDialog(): Promise<DG.Dialog> {
|
|
17
|
+
const targetColumns = grok.shell.t.columns.bySemTypeAll(DG.SEMTYPE.MACROMOLECULE);
|
|
18
|
+
if (!targetColumns)
|
|
19
|
+
throw new Error('No dataframe with macromolecule columns open');
|
|
20
|
+
|
|
21
|
+
const targetColumnInput = ui.columnInput(
|
|
22
|
+
'Column', grok.shell.t, targetColumns[0], null,
|
|
23
|
+
{filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE}
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const generateHelmChoiceInput = ui.boolInput('Get HELM', true);
|
|
27
|
+
ui.tooltip.bind(generateHelmChoiceInput.root, 'Add HELM column');
|
|
28
|
+
|
|
29
|
+
const chiralityEngineInput = ui.boolInput('Chirality engine', false);
|
|
30
|
+
const ruleInputs = new RuleInputs(RULES_PATH, RULES_STORAGE_NAME, '.csv');
|
|
31
|
+
const rulesForm = await ruleInputs.getForm();
|
|
32
|
+
|
|
33
|
+
const div = ui.div([
|
|
34
|
+
targetColumnInput,
|
|
35
|
+
generateHelmChoiceInput,
|
|
36
|
+
chiralityEngineInput,
|
|
37
|
+
'Rules used',
|
|
38
|
+
rulesForm
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
const dialog = ui.dialog('Poly Tool')
|
|
42
|
+
.add(div)
|
|
43
|
+
.onOK(async () => {
|
|
44
|
+
const molCol = targetColumnInput.value;
|
|
45
|
+
if (!molCol) {
|
|
46
|
+
grok.shell.warning('No marcomolecule column chosen!');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const files = await ruleInputs.getActive();
|
|
51
|
+
|
|
52
|
+
addTransformedColumn(molCol!,
|
|
53
|
+
generateHelmChoiceInput.value!,
|
|
54
|
+
files,
|
|
55
|
+
chiralityEngineInput.value!);
|
|
86
56
|
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
private async getRuleFilesBlock(): Promise<DG.InputBase<boolean | null>[]> {
|
|
90
|
-
this.ruleFiles = await getAllAvailableRuleFiles();
|
|
91
|
-
this.userRuleSettings = await getUserRulesSettings();
|
|
92
|
-
const cBoxes: DG.InputBase<boolean | null>[] = [];
|
|
93
|
-
|
|
94
|
-
for (let i = 0; i < this.ruleFiles.length; i++) {
|
|
95
|
-
const ruleFileName = this.ruleFiles[i];
|
|
96
|
-
const isRuleFileSelected = this.userRuleSettings.included.includes(ruleFileName);
|
|
97
|
-
const cb = ui.boolInput(
|
|
98
|
-
ruleFileName.replace(RULES_PATH, ''),
|
|
99
|
-
isRuleFileSelected,
|
|
100
|
-
(isSelected: boolean) => this.updateRulesSelectionStatus(ruleFileName, isSelected)
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
cBoxes.push(cb);
|
|
104
|
-
}
|
|
105
|
-
return cBoxes;
|
|
106
|
-
}
|
|
107
57
|
|
|
108
|
-
|
|
109
|
-
const targetColumns = grok.shell.t.columns.bySemTypeAll(DG.SEMTYPE.MACROMOLECULE);
|
|
110
|
-
if (!targetColumns)
|
|
111
|
-
throw new Error('No dataframe with macromolecule columns open');
|
|
112
|
-
|
|
113
|
-
const targetColumnInput = ui.columnInput(
|
|
114
|
-
'Column', grok.shell.t, targetColumns[0], null,
|
|
115
|
-
{filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE}
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
const generateHelmChoiceInput = ui.boolInput('Get HELM', true);
|
|
119
|
-
ui.tooltip.bind(generateHelmChoiceInput.root, 'Add HELM column');
|
|
120
|
-
|
|
121
|
-
const addButton = this.getAddButton();
|
|
122
|
-
this.ruleFilesInputs = ui.div(await this.getRuleFilesBlock());
|
|
123
|
-
//const rulesFiles = ui.div(this.ruleFilesInputs);
|
|
124
|
-
const chiralityEngineInput = ui.boolInput('Chirality engine', false);
|
|
125
|
-
|
|
126
|
-
const div = ui.div([
|
|
127
|
-
targetColumnInput,
|
|
128
|
-
generateHelmChoiceInput,
|
|
129
|
-
chiralityEngineInput,
|
|
130
|
-
'Rules used',
|
|
131
|
-
this.ruleFilesInputs,
|
|
132
|
-
addButton
|
|
133
|
-
]);
|
|
134
|
-
|
|
135
|
-
this.dialog = ui.dialog('Poly Tool')
|
|
136
|
-
.add(div)
|
|
137
|
-
.onOK(async () => {
|
|
138
|
-
const molCol = targetColumnInput.value;
|
|
139
|
-
if (!molCol) {
|
|
140
|
-
grok.shell.warning('No marcomolecule column chosen!');
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
addTransformedColumn(molCol!, generateHelmChoiceInput.value!, this.userRuleSettings.included, chiralityEngineInput.value!);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
return this.dialog;
|
|
147
|
-
}
|
|
58
|
+
return dialog;
|
|
148
59
|
}
|
|
@@ -3,8 +3,8 @@ import * as ui from 'datagrok-api/ui';
|
|
|
3
3
|
import * as grok from 'datagrok-api/grok';
|
|
4
4
|
|
|
5
5
|
import wu from 'wu';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
|
|
7
|
+
import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
|
|
8
8
|
|
|
9
9
|
const FASTA_LINE_WIDTH = 60;
|
|
10
10
|
|
|
@@ -28,8 +28,8 @@ export function saveAsFastaUI() {
|
|
|
28
28
|
.filter((gc: DG.GridColumn) => {
|
|
29
29
|
const col: DG.Column | null = gc.column;
|
|
30
30
|
if (col && col.semType === DG.SEMTYPE.MACROMOLECULE) {
|
|
31
|
-
const
|
|
32
|
-
return
|
|
31
|
+
const sh = SeqHandler.forColumn(col);
|
|
32
|
+
return sh.isFasta();
|
|
33
33
|
}
|
|
34
34
|
return false;
|
|
35
35
|
}).toArray();
|
|
@@ -77,17 +77,16 @@ export function saveAsFastaUI() {
|
|
|
77
77
|
export function saveAsFastaDo(
|
|
78
78
|
idColList: DG.Column[], seqCol: DG.Column, lineWidth: number = FASTA_LINE_WIDTH, lineSeparator: string = '\n',
|
|
79
79
|
): string {
|
|
80
|
-
const
|
|
81
|
-
|
|
80
|
+
const sh = SeqHandler.forColumn(seqCol);
|
|
82
81
|
const fastaLines: string[] = [];
|
|
83
82
|
|
|
84
|
-
for (let
|
|
83
|
+
for (let rowIdx: number = 0; rowIdx < seqCol.length; rowIdx++) {
|
|
85
84
|
// multiple identifiers separated by vertical bars
|
|
86
85
|
// https://en.wikipedia.org/wiki/FASTA_format
|
|
87
86
|
|
|
88
|
-
const seqId: string = idColList.map((col) => col.get(
|
|
89
|
-
const
|
|
90
|
-
const seqLineList: string[] = wrapSequence(
|
|
87
|
+
const seqId: string = idColList.map((col) => col.get(rowIdx).toString()).join('|');
|
|
88
|
+
const srcSS = sh.getSplitted(rowIdx);
|
|
89
|
+
const seqLineList: string[] = wrapSequence(srcSS, lineWidth);
|
|
91
90
|
|
|
92
91
|
fastaLines.push(`>${seqId}${lineSeparator}`);
|
|
93
92
|
for (const line of seqLineList)
|
|
@@ -99,16 +98,16 @@ export function saveAsFastaDo(
|
|
|
99
98
|
}
|
|
100
99
|
|
|
101
100
|
/* split sequence for monomers to prevent wrapping monomer partially */
|
|
102
|
-
export function wrapSequence(
|
|
103
|
-
const seqMonomerList = splitter(seq);
|
|
101
|
+
export function wrapSequence(srcSS: ISeqSplitted, lineWidth: number = FASTA_LINE_WIDTH): string[] {
|
|
104
102
|
let seqPos: number = 0;
|
|
105
|
-
const seqLength: number =
|
|
103
|
+
const seqLength: number = srcSS.length;
|
|
106
104
|
|
|
107
105
|
const seqLineList: string[] = [];
|
|
108
106
|
while (seqPos < seqLength) {
|
|
109
107
|
/* join sliced monomer into line */
|
|
110
|
-
const seqLine
|
|
111
|
-
const seqLineTxt: string = seqLine.map((
|
|
108
|
+
const seqLine = wu(srcSS.originals).slice(seqPos, seqPos + lineWidth).toArray();
|
|
109
|
+
const seqLineTxt: string = seqLine.map((om) => om.length > 1 ? `[${om}]` : om)
|
|
110
|
+
.reduce((a, b) => a + b, '');
|
|
112
111
|
seqLineList.push(seqLineTxt);
|
|
113
112
|
seqPos += seqLine.length;
|
|
114
113
|
}
|
|
@@ -8,7 +8,7 @@ import {helm2mol} from './helm-to-molfile';
|
|
|
8
8
|
import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
|
|
9
9
|
import {checkInputColumnUI} from './check-input-column';
|
|
10
10
|
import {getMonomerLibHelper} from '../package';
|
|
11
|
-
import {
|
|
11
|
+
import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
|
|
12
12
|
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
13
13
|
|
|
14
14
|
export async function sequenceToMolfile(df: DG.DataFrame, macroMolecule: DG.Column, nonlinear: boolean): Promise<void> {
|
|
@@ -17,9 +17,9 @@ export async function sequenceToMolfile(df: DG.DataFrame, macroMolecule: DG.Colu
|
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
19
|
if (nonlinear) {
|
|
20
|
-
const
|
|
21
|
-
if (!
|
|
22
|
-
macroMolecule =
|
|
20
|
+
const seqSh = SeqHandler.forColumn(macroMolecule);
|
|
21
|
+
if (!seqSh.isHelm())
|
|
22
|
+
macroMolecule = seqSh.convert(NOTATION.HELM);
|
|
23
23
|
helm2mol(df, macroMolecule);
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
@@ -6,7 +6,7 @@ import $ from 'cash-dom';
|
|
|
6
6
|
import wu from 'wu';
|
|
7
7
|
import {fromEvent, Observable, Subject, Unsubscribable} from 'rxjs';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
|
|
10
10
|
import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
|
|
11
11
|
import {
|
|
12
12
|
monomerToShort, pickUpPalette, pickUpSeqCol, TAGS as bioTAGS, positionSeparator
|
|
@@ -17,8 +17,7 @@ import {
|
|
|
17
17
|
} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
18
18
|
import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
|
|
19
19
|
import {intToHtmlA} from '@datagrok-libraries/utils/src/color';
|
|
20
|
-
import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
|
|
21
|
-
import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
|
|
20
|
+
import {GAP_SYMBOL, ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
|
|
22
21
|
import {testEvent} from '@datagrok-libraries/utils/src/test';
|
|
23
22
|
import {PromiseSyncer} from '@datagrok-libraries/bio/src/utils/syncer';
|
|
24
23
|
|
|
@@ -26,7 +25,6 @@ import {AggFunc, getAgg} from '../utils/agg';
|
|
|
26
25
|
import {buildCompositionTable} from '../widgets/composition-analysis-widget';
|
|
27
26
|
|
|
28
27
|
import {_package} from '../package';
|
|
29
|
-
import {GAP_SYMBOL} from '../const';
|
|
30
28
|
|
|
31
29
|
declare global {
|
|
32
30
|
interface HTMLCanvasElement {
|
|
@@ -168,8 +166,7 @@ export class PositionInfo {
|
|
|
168
166
|
}
|
|
169
167
|
}
|
|
170
168
|
|
|
171
|
-
calcScreen(
|
|
172
|
-
isGap: (m: string) => boolean, posIdx: number, firstVisiblePosIdx: number,
|
|
169
|
+
calcScreen(posIdx: number, firstVisiblePosIdx: number,
|
|
173
170
|
absoluteMaxHeight: number, heightMode: PositionHeight, alphabetSizeLog: number,
|
|
174
171
|
positionWidthWithMargin: number, positionWidth: number, dpr: number, positionLabelsHeight: number
|
|
175
172
|
): void {
|
|
@@ -180,13 +177,13 @@ export class PositionInfo {
|
|
|
180
177
|
|
|
181
178
|
const entries = Object.entries(this._freqs)
|
|
182
179
|
.sort((a, b) => {
|
|
183
|
-
if (
|
|
180
|
+
if (a[0] !== GAP_SYMBOL && b[0] !== GAP_SYMBOL)
|
|
184
181
|
return b[1].value - a[1].value;
|
|
185
|
-
else if (
|
|
182
|
+
else if (a[0] === GAP_SYMBOL && b[0] === GAP_SYMBOL)
|
|
186
183
|
return 0;
|
|
187
|
-
else if (
|
|
184
|
+
else if (a[0] === GAP_SYMBOL)
|
|
188
185
|
return -1;
|
|
189
|
-
else /* (
|
|
186
|
+
else /* (b[0] === GAP_SYMBOL) */
|
|
190
187
|
return +1;
|
|
191
188
|
});
|
|
192
189
|
for (const [_m, pmi] of entries) {
|
|
@@ -200,11 +197,10 @@ export class PositionInfo {
|
|
|
200
197
|
}
|
|
201
198
|
|
|
202
199
|
render(g: CanvasRenderingContext2D,
|
|
203
|
-
isGap: (m: string) => boolean,
|
|
204
200
|
fontStyle: string, uppercaseLetterAscent: number, uppercaseLetterHeight: number, cp: SeqPalette
|
|
205
201
|
) {
|
|
206
202
|
for (const [monomer, pmInfo] of Object.entries(this._freqs)) {
|
|
207
|
-
if (
|
|
203
|
+
if (monomer !== GAP_SYMBOL) {
|
|
208
204
|
const monomerTxt = monomerToShort(monomer, 5);
|
|
209
205
|
const b = pmInfo.bounds!;
|
|
210
206
|
const left = b.left;
|
|
@@ -303,7 +299,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
303
299
|
|
|
304
300
|
private viewed: boolean = false;
|
|
305
301
|
|
|
306
|
-
private
|
|
302
|
+
private seqHandler: SeqHandler | null;
|
|
307
303
|
private initialized: boolean = false;
|
|
308
304
|
|
|
309
305
|
// private readonly colorScheme: ColorScheme = ColorSchemes[NucleotidesWebLogo.residuesSet];
|
|
@@ -373,7 +369,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
373
369
|
}
|
|
374
370
|
|
|
375
371
|
public get positionMarginValue(): number {
|
|
376
|
-
if (this.positionMarginState === PositionMarginStates.AUTO && this.
|
|
372
|
+
if (this.positionMarginState === PositionMarginStates.AUTO && this.seqHandler!.getAlphabetIsMultichar() === true)
|
|
377
373
|
return this.positionMargin;
|
|
378
374
|
else if (this.positionMarginState === PositionMarginStates.ON)
|
|
379
375
|
return this.positionMargin;
|
|
@@ -385,7 +381,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
385
381
|
super();
|
|
386
382
|
|
|
387
383
|
this.textBaseline = 'top';
|
|
388
|
-
this.
|
|
384
|
+
this.seqHandler = null;
|
|
389
385
|
|
|
390
386
|
// -- Data --
|
|
391
387
|
this.sequenceColumnName = this.string(PROPS.sequenceColumnName, defaults.sequenceColumnName,
|
|
@@ -589,7 +585,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
589
585
|
}
|
|
590
586
|
if (this.seqCol) {
|
|
591
587
|
try {
|
|
592
|
-
this.
|
|
588
|
+
this.seqHandler = SeqHandler.forColumn(this.seqCol);
|
|
593
589
|
|
|
594
590
|
this.palette = pickUpPalette(this.seqCol);
|
|
595
591
|
this.render(WlRenderLevel.Freqs, 'updateSeqCol()');
|
|
@@ -600,7 +596,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
600
596
|
throw err;
|
|
601
597
|
}
|
|
602
598
|
if (!this.seqCol) {
|
|
603
|
-
this.
|
|
599
|
+
this.seqHandler = null;
|
|
604
600
|
this.positionNames = [];
|
|
605
601
|
this.positionLabels = [];
|
|
606
602
|
this.startPosition = -1;
|
|
@@ -817,6 +813,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
817
813
|
case PROPS.sequenceColumnName:
|
|
818
814
|
this.updateSeqCol();
|
|
819
815
|
break;
|
|
816
|
+
case PROPS.sequenceColumnName:
|
|
820
817
|
case PROPS.startPositionName:
|
|
821
818
|
case PROPS.endPositionName:
|
|
822
819
|
case PROPS.filterSource:
|
|
@@ -956,9 +953,10 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
956
953
|
// region updatePositions
|
|
957
954
|
|
|
958
955
|
const dfFilter = this.getFilter();
|
|
959
|
-
const maxLength: number = dfFilter.trueCount === 0 ? this.
|
|
960
|
-
wu.
|
|
961
|
-
|
|
956
|
+
const maxLength: number = dfFilter.trueCount === 0 ? this.seqHandler!.maxLength :
|
|
957
|
+
wu.count(0).take(this.seqHandler!.length).map((rowIdx) => {
|
|
958
|
+
const mList = this.seqHandler!.getSplitted(rowIdx);
|
|
959
|
+
return dfFilter.get(rowIdx) && !!mList ? mList.length : 0;
|
|
962
960
|
}).reduce((max, l) => Math.max(max, l), 0);
|
|
963
961
|
|
|
964
962
|
/** positionNames and positionLabel can be set up through the column's tags only */
|
|
@@ -979,7 +977,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
979
977
|
// endregion updatePositions
|
|
980
978
|
|
|
981
979
|
const length: number = this.startPosition <= this.endPosition ? this.endPosition - this.startPosition + 1 : 0;
|
|
982
|
-
this.
|
|
980
|
+
this.seqHandler = SeqHandler.forColumn(this.seqCol);
|
|
983
981
|
const posCount: number = this.startPosition <= this.endPosition ? this.endPosition - this.startPosition + 1 : 0;
|
|
984
982
|
this.positions = new Array(posCount);
|
|
985
983
|
for (let jPos = 0; jPos < length; jPos++) {
|
|
@@ -991,18 +989,16 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
991
989
|
|
|
992
990
|
// 2022-05-05 askalkin instructed to show WebLogo based on filter (not selection)
|
|
993
991
|
const dfRowCount = this.dataFrame.rowCount;
|
|
994
|
-
const splitted = this.unitsHandler.splitted;
|
|
995
992
|
|
|
996
993
|
for (let jPos = 0; jPos < length; ++jPos) {
|
|
994
|
+
const pi = this.positions[jPos];
|
|
997
995
|
// Here we want to build lists of values for every monomer in position jPos
|
|
998
996
|
for (let rowI = 0; rowI < dfRowCount; ++rowI) {
|
|
999
997
|
if (dfFilter.get(rowI)) {
|
|
1000
|
-
const seqMList: ISeqSplitted = splitted[rowI];
|
|
1001
|
-
const om: string = seqMList[this.startPosition + jPos] || this.unitsHandler.defaultGapSymbol;
|
|
1002
|
-
const cm: string = this.unitsHandler?.defaultGapSymbol === om ? GAP_SYMBOL : om;
|
|
1003
|
-
const pi = this.positions[jPos];
|
|
1004
|
-
const pmi = pi.getFreq(cm);
|
|
1005
998
|
++pi.sumRowCount;
|
|
999
|
+
const seqMList: ISeqSplitted = this.seqHandler.getSplitted(rowI);
|
|
1000
|
+
const cm: string = seqMList.getCanonical(this.startPosition + jPos);
|
|
1001
|
+
const pmi = pi.getFreq(cm);
|
|
1006
1002
|
pmi.value = ++pmi.rowCount;
|
|
1007
1003
|
}
|
|
1008
1004
|
}
|
|
@@ -1019,10 +1015,10 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
1019
1015
|
|
|
1020
1016
|
for (let rowI = 0; rowI < dfRowCount; ++rowI) {
|
|
1021
1017
|
if (dfFilter.get(rowI)) { // respect the filter
|
|
1022
|
-
const seqMList: ISeqSplitted =
|
|
1023
|
-
const
|
|
1018
|
+
const seqMList: ISeqSplitted = this.seqHandler.getSplitted(rowI);
|
|
1019
|
+
const cm: string = seqMList.getCanonical(this.startPosition + jPos);
|
|
1024
1020
|
const value: number | null = valueCol.get(rowI);
|
|
1025
|
-
this.positions[jPos].getFreq(
|
|
1021
|
+
this.positions[jPos].getFreq(cm).push(value);
|
|
1026
1022
|
}
|
|
1027
1023
|
}
|
|
1028
1024
|
this.positions[jPos].aggregate(this.valueAggrType);
|
|
@@ -1061,7 +1057,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
1061
1057
|
`this.positions.length = ${this.positions.length}, jPos = ${jPos}`);
|
|
1062
1058
|
continue;
|
|
1063
1059
|
}
|
|
1064
|
-
this.positions[jPos].calcScreen(
|
|
1060
|
+
this.positions[jPos].calcScreen(
|
|
1065
1061
|
jPos, this.slider.min, absoluteMaxHeight, this.positionHeight,
|
|
1066
1062
|
alphabetSizeLog, this._positionWidthWithMargin, this._positionWidth, dpr, positionLabelsHeight);
|
|
1067
1063
|
}
|
|
@@ -1117,10 +1113,8 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
1117
1113
|
// Hacks to scale uppercase characters to target rectangle
|
|
1118
1114
|
const uppercaseLetterAscent = 0.25;
|
|
1119
1115
|
const uppercaseLetterHeight = 12.2;
|
|
1120
|
-
for (let jPos = firstPos; jPos <= lastPos; jPos++)
|
|
1121
|
-
this.positions[jPos].render(g,
|
|
1122
|
-
fontStyle, uppercaseLetterAscent, uppercaseLetterHeight, this.palette);
|
|
1123
|
-
}
|
|
1116
|
+
for (let jPos = firstPos; jPos <= lastPos; jPos++)
|
|
1117
|
+
this.positions[jPos].render(g, fontStyle, uppercaseLetterAscent, uppercaseLetterHeight, this.palette);
|
|
1124
1118
|
} finally {
|
|
1125
1119
|
g.restore();
|
|
1126
1120
|
}
|
|
@@ -1135,18 +1129,16 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
1135
1129
|
return;
|
|
1136
1130
|
}
|
|
1137
1131
|
this.requestedRenderLevel = WlRenderLevel.None;
|
|
1138
|
-
this.
|
|
1139
|
-
.
|
|
1140
|
-
|
|
1141
|
-
_package.logger.error(errMsg, undefined, errStack);
|
|
1142
|
-
});
|
|
1132
|
+
this.viewSyncer.sync(logPrefix, async () => {
|
|
1133
|
+
await this.renderInt(renderLevel);
|
|
1134
|
+
});
|
|
1143
1135
|
}
|
|
1144
1136
|
|
|
1145
1137
|
private _lastWidth: number;
|
|
1146
1138
|
private _lastHeight: number;
|
|
1147
1139
|
|
|
1148
1140
|
public getAlphabetSize(): number {
|
|
1149
|
-
return this.
|
|
1141
|
+
return this.seqHandler?.getAlphabetSize() ?? 0;
|
|
1150
1142
|
}
|
|
1151
1143
|
|
|
1152
1144
|
// -- Handle events --
|
|
@@ -1212,10 +1204,10 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
1212
1204
|
tooltipRows.push(pi.buildCompositionTable(this.palette!));
|
|
1213
1205
|
const tooltipEl = ui.divV(tooltipRows);
|
|
1214
1206
|
ui.tooltip.show(tooltipEl, args.x + 16, args.y + 16);
|
|
1215
|
-
} else if (pi !== null && monomer && this.dataFrame && this.seqCol && this.
|
|
1207
|
+
} else if (pi !== null && monomer && this.dataFrame && this.seqCol && this.seqHandler) {
|
|
1216
1208
|
// Monomer at position tooltip
|
|
1217
1209
|
// const monomerAtPosSeqCount = countForMonomerAtPosition(
|
|
1218
|
-
// this.dataFrame, this.
|
|
1210
|
+
// this.dataFrame, this.seqHandler!, this.getFilter(), monomer, atPI);
|
|
1219
1211
|
const pmi = pi.getFreq(monomer);
|
|
1220
1212
|
|
|
1221
1213
|
const tooltipRows = [
|
|
@@ -1243,10 +1235,10 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
1243
1235
|
const [pi, monomer] = this.getMonomer(this.canvas.getCursorPosition(args, dpr), dpr);
|
|
1244
1236
|
|
|
1245
1237
|
// prevents deselect all rows if we miss monomer bounds
|
|
1246
|
-
if (pi !== null && monomer !== null && this.dataFrame && this.seqCol && this.
|
|
1238
|
+
if (pi !== null && monomer !== null && this.dataFrame && this.seqCol && this.seqHandler) {
|
|
1247
1239
|
// Calculate a new BitSet object for selection to prevent interfering with existing
|
|
1248
1240
|
const selBS: DG.BitSet = DG.BitSet.create(this.dataFrame.selection.length, (rowI: number) => {
|
|
1249
|
-
return checkSeqForMonomerAtPos(this.dataFrame, this.
|
|
1241
|
+
return checkSeqForMonomerAtPos(this.dataFrame, this.seqHandler!, this.getFilter(), rowI, monomer, pi);
|
|
1250
1242
|
});
|
|
1251
1243
|
this.dataFrame.selection.init((i) => selBS.get(i));
|
|
1252
1244
|
}
|
|
@@ -1281,8 +1273,8 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
1281
1273
|
const callLog = `invalidate(${caller ? ` <- ${caller} ` : ''})`;
|
|
1282
1274
|
const logPrefix = `${this.viewerToLog()}.${callLog}`;
|
|
1283
1275
|
// Put the event trigger in the tail of the synced calls queue.
|
|
1276
|
+
this.render(WlRenderLevel.None, callLog); // Put render request to the syncer
|
|
1284
1277
|
this.viewSyncer.sync(`${logPrefix}`, async () => {
|
|
1285
|
-
this.render(WlRenderLevel.None, callLog);
|
|
1286
1278
|
this._onRendered.next();
|
|
1287
1279
|
});
|
|
1288
1280
|
}
|
|
@@ -1333,23 +1325,23 @@ function renderPositionLabels(g: CanvasRenderingContext2D,
|
|
|
1333
1325
|
}
|
|
1334
1326
|
|
|
1335
1327
|
export function checkSeqForMonomerAtPos(
|
|
1336
|
-
df: DG.DataFrame,
|
|
1328
|
+
df: DG.DataFrame, sh: SeqHandler, filter: DG.BitSet, rowI: number, monomer: string, at: PositionInfo,
|
|
1337
1329
|
): boolean {
|
|
1338
|
-
const seqMList: ISeqSplitted =
|
|
1339
|
-
const
|
|
1340
|
-
return
|
|
1330
|
+
const seqMList: ISeqSplitted = sh.getSplitted(rowI);
|
|
1331
|
+
const seqCM: string | null = at.pos < seqMList.length ? seqMList.getCanonical(at.pos) : null;
|
|
1332
|
+
return seqCM !== null && seqCM === monomer;
|
|
1341
1333
|
}
|
|
1342
1334
|
|
|
1343
1335
|
export function countForMonomerAtPosition(
|
|
1344
|
-
df: DG.DataFrame,
|
|
1336
|
+
df: DG.DataFrame, sh: SeqHandler, filter: DG.BitSet, monomer: string, at: PositionInfo
|
|
1345
1337
|
): number {
|
|
1346
1338
|
let count = 0;
|
|
1347
1339
|
let rowI = -1;
|
|
1348
1340
|
while ((rowI = filter.findNext(rowI, true)) != -1) {
|
|
1349
|
-
const seqMList: ISeqSplitted =
|
|
1341
|
+
const seqMList: ISeqSplitted = sh.getSplitted(rowI);
|
|
1350
1342
|
const seqMPos: number = at.pos;
|
|
1351
|
-
const
|
|
1352
|
-
if (
|
|
1343
|
+
const seqCM: string | null = seqMPos < seqMList.length ? seqMList.getCanonical(seqMPos) : null;
|
|
1344
|
+
if (seqCM !== null && seqCM === monomer) count++;
|
|
1353
1345
|
}
|
|
1354
1346
|
return count;
|
|
1355
1347
|
}
|