@datagrok/bio 2.15.7 → 2.15.8
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 +13 -0
- package/dist/package-test.js +2 -2
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +2 -2
- package/dist/package.js.map +1 -1
- package/package.json +4 -4
- package/src/analysis/sequence-activity-cliffs.ts +10 -5
- package/src/analysis/sequence-similarity-viewer.ts +1 -1
- package/src/package.ts +1 -1
- package/src/tests/WebLogo-positions-test.ts +1 -1
- package/src/tests/splitters-test.ts +23 -6
- package/src/utils/cell-renderer.ts +101 -111
- package/src/utils/monomer-cell-renderer-base.ts +30 -0
- package/src/utils/monomer-cell-renderer.ts +74 -54
- package/src/utils/monomer-lib/lib-manager.ts +1 -1
- package/src/utils/monomer-lib/monomer-colors.ts +33 -30
- package/src/utils/monomer-lib/monomer-lib-base.ts +62 -10
- package/src/utils/monomer-lib/monomer-lib.ts +7 -50
- package/src/utils/monomer-lib/web-editor-monomer-dummy.ts +11 -4
- package/src/utils/monomer-lib/web-editor-monomer-of-library.ts +19 -12
- package/src/viewers/web-logo-viewer.ts +40 -27
- package/src/widgets/composition-analysis-widget.ts +12 -17
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"name": "Aleksandr Tanas",
|
|
6
6
|
"email": "atanas@datagrok.ai"
|
|
7
7
|
},
|
|
8
|
-
"version": "2.15.
|
|
8
|
+
"version": "2.15.8",
|
|
9
9
|
"description": "Bioinformatics support (import/export of sequences, conversion, visualization, analysis). [See more](https://github.com/datagrok-ai/public/blob/master/packages/Bio/README.md) for details.",
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@biowasm/aioli": "^3.1.0",
|
|
40
|
-
"@datagrok-libraries/bio": "^5.44.
|
|
40
|
+
"@datagrok-libraries/bio": "^5.44.4",
|
|
41
41
|
"@datagrok-libraries/chem-meta": "^1.2.7",
|
|
42
42
|
"@datagrok-libraries/math": "^1.2.0",
|
|
43
43
|
"@datagrok-libraries/ml": "^6.7.1",
|
|
@@ -60,9 +60,9 @@
|
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@datagrok-libraries/helm-web-editor": "^1.1.11",
|
|
62
62
|
"@datagrok-libraries/js-draw-lite": "^0.0.8",
|
|
63
|
-
"@datagrok/chem": "^1.12.
|
|
63
|
+
"@datagrok/chem": "^1.12.3",
|
|
64
64
|
"@datagrok/dendrogram": "^1.2.33",
|
|
65
|
-
"@datagrok/helm": "^2.5.
|
|
65
|
+
"@datagrok/helm": "^2.5.3",
|
|
66
66
|
"@types/node": "^17.0.24",
|
|
67
67
|
"@types/wu": "latest",
|
|
68
68
|
"@typescript-eslint/eslint-plugin": "latest",
|
|
@@ -12,6 +12,9 @@ import {invalidateMols, MONOMERIC_COL_TAGS} from '../substructure-search/substru
|
|
|
12
12
|
import {TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
13
13
|
import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
|
|
14
14
|
import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
|
|
15
|
+
import {HelmType} from '@datagrok-libraries/bio/src/helm/types';
|
|
16
|
+
|
|
17
|
+
import {getMonomerLib} from '../package';
|
|
15
18
|
|
|
16
19
|
export async function getDistances(col: DG.Column, seq: string): Promise<Array<number>> {
|
|
17
20
|
const stringArray = col.toList();
|
|
@@ -106,9 +109,10 @@ export function createPropPanelElement(params: ITooltipAndPanelParams): HTMLDivE
|
|
|
106
109
|
|
|
107
110
|
const molDifferences: { [key: number]: HTMLCanvasElement } = {};
|
|
108
111
|
const sh = SeqHandler.forColumn(params.seqCol);
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
+
const biotype = sh.defaultBiotype;
|
|
113
|
+
const subParts1 = sh.getSplitted(params.points[0]);
|
|
114
|
+
const subParts2 = sh.getSplitted(params.points[1]);
|
|
115
|
+
const canvas = createDifferenceCanvas(subParts1, subParts2, biotype, molDifferences);
|
|
112
116
|
propPanel.append(ui.div(canvas, {style: {width: '300px', overflow: 'scroll'}}));
|
|
113
117
|
|
|
114
118
|
propPanel.append(createDifferencesWithPositions(molDifferences));
|
|
@@ -129,15 +133,16 @@ function createPropPanelField(name: string, value: number): HTMLDivElement {
|
|
|
129
133
|
export function createDifferenceCanvas(
|
|
130
134
|
subParts1: ISeqSplitted,
|
|
131
135
|
subParts2: ISeqSplitted,
|
|
132
|
-
|
|
136
|
+
biotype: HelmType,
|
|
133
137
|
molDifferences: { [key: number]: HTMLCanvasElement }): HTMLCanvasElement {
|
|
134
138
|
const canvas = document.createElement('canvas');
|
|
135
139
|
const context = canvas.getContext('2d');
|
|
136
140
|
canvas.height = 30;
|
|
141
|
+
const monomerLib = getMonomerLib();
|
|
137
142
|
drawMoleculeDifferenceOnCanvas(context!, 0, 0, 0, 30,
|
|
138
143
|
wu.count(0).take(subParts1.length).map((posIdx) => subParts1.getCanonical(posIdx)).toArray(),
|
|
139
144
|
wu.count(0).take(subParts2.length).map((posIdx) => subParts2.getCanonical(posIdx)).toArray(),
|
|
140
|
-
|
|
145
|
+
biotype, monomerLib, true, molDifferences);
|
|
141
146
|
return canvas;
|
|
142
147
|
}
|
|
143
148
|
|
|
@@ -130,7 +130,7 @@ export class SequenceSimilarityViewer extends SequenceSearchBaseViewer {
|
|
|
130
130
|
const subParts1 = molColSh.getSplitted(this.targetMoleculeIdx);
|
|
131
131
|
const subParts2 = resSh.getSplitted(resDf.currentRowIdx);
|
|
132
132
|
const alignment = alignSequencePair(subParts1, subParts2);
|
|
133
|
-
const canvas = createDifferenceCanvas(alignment.seq1Splitted, alignment.seq2Splitted, resSh.
|
|
133
|
+
const canvas = createDifferenceCanvas(alignment.seq1Splitted, alignment.seq2Splitted, resSh.defaultBiotype, molDifferences);
|
|
134
134
|
propPanel.append(ui.div(canvas, {style: {width: '300px', overflow: 'scroll'}}));
|
|
135
135
|
if (subParts1.length !== subParts2.length) {
|
|
136
136
|
propPanel.append(ui.divV([
|
package/src/package.ts
CHANGED
|
@@ -364,7 +364,7 @@ export function macroMolColumnPropertyPanel(molColumn: DG.Column): DG.Widget {
|
|
|
364
364
|
//input: semantic_value sequence { semType: Macromolecule }
|
|
365
365
|
//output: widget result
|
|
366
366
|
export function compositionAnalysisWidget(sequence: DG.SemanticValue): DG.Widget {
|
|
367
|
-
return getCompositionAnalysisWidget(sequence);
|
|
367
|
+
return getCompositionAnalysisWidget(sequence, monomerLib!);
|
|
368
368
|
}
|
|
369
369
|
|
|
370
370
|
//name: MacromoleculeDifferenceCellRenderer
|
|
@@ -36,7 +36,7 @@ ATC-G-TTGC--
|
|
|
36
36
|
const wlViewer: WebLogoViewer = (await df.plot.fromType('WebLogo')) as WebLogoViewer;
|
|
37
37
|
await testEvent(wlViewer.onLayoutCalculated, () => {}, () => {
|
|
38
38
|
tv.dockManager.dock(wlViewer.root, DG.DOCK_TYPE.DOWN);
|
|
39
|
-
}, 500);
|
|
39
|
+
}, 500, 'Layout calculate timeout');
|
|
40
40
|
const positions: PI[] = wlViewer['positions'];
|
|
41
41
|
|
|
42
42
|
const resAllDf1: PI[] = [
|
|
@@ -4,20 +4,21 @@ import * as DG from 'datagrok-api/dg';
|
|
|
4
4
|
|
|
5
5
|
import wu from 'wu';
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
after, before, category, test, expect, expectArray
|
|
9
|
-
} from '@datagrok-libraries/utils/src/test';
|
|
7
|
+
import {after, before, category, test, expect, expectArray} from '@datagrok-libraries/utils/src/test';
|
|
10
8
|
import {TAGS as bioTAGS, splitterAsFasta} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
9
|
+
import {splitterAsHelm} from '@datagrok-libraries/bio/src/utils/macromolecule/utils';
|
|
10
|
+
import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
|
|
11
|
+
import {IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
12
|
+
import {UserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/types';
|
|
13
|
+
import {getMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
11
14
|
|
|
12
15
|
import {splitToMonomersUI} from '../utils/split-to-monomers';
|
|
13
16
|
import {awaitGrid} from './utils';
|
|
14
17
|
import * as C from '../utils/constants';
|
|
15
18
|
import {getHelmMonomers} from '../package';
|
|
16
19
|
|
|
17
|
-
import {splitterAsHelm} from '@datagrok-libraries/bio/src/utils/macromolecule/utils';
|
|
18
|
-
import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
|
|
19
|
-
|
|
20
20
|
import {_package} from '../package-test';
|
|
21
|
+
import {getUserLibSettings, setUserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
|
|
21
22
|
|
|
22
23
|
category('splitters', async () => {
|
|
23
24
|
before(async () => {
|
|
@@ -85,7 +86,23 @@ category('splitters', async () => {
|
|
|
85
86
|
test('testHelm1', async () => { await _testHelmSplitter(data.testHelm1[0], data.testHelm1[1]); });
|
|
86
87
|
test('testHelm2', async () => { await _testHelmSplitter(data.testHelm2[0], data.testHelm2[1]); });
|
|
87
88
|
test('testHelm3', async () => { await _testHelmSplitter(data.testHelm3[0], data.testHelm3[1]); });
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
category('splitters', () => {
|
|
92
|
+
let monomerLibHelper: IMonomerLibHelper;
|
|
93
|
+
let userLibSettings: UserLibSettings;
|
|
88
94
|
|
|
95
|
+
before(async () => {
|
|
96
|
+
monomerLibHelper = await getMonomerLibHelper();
|
|
97
|
+
userLibSettings = await getUserLibSettings();
|
|
98
|
+
|
|
99
|
+
await monomerLibHelper.loadMonomerLibForTests();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
after(async () => {
|
|
103
|
+
await setUserLibSettings(userLibSettings);
|
|
104
|
+
await monomerLibHelper.loadMonomerLib(true);
|
|
105
|
+
});
|
|
89
106
|
|
|
90
107
|
test('splitToMonomers', async () => {
|
|
91
108
|
const df: DG.DataFrame = await grok.dapi.files.readCsv('System:AppData/Bio/samples/MSA.csv');
|
|
@@ -7,34 +7,31 @@ import * as ui from 'datagrok-api/ui';
|
|
|
7
7
|
|
|
8
8
|
import wu from 'wu';
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import {HelmType} from '@datagrok-libraries/bio/src/helm/types';
|
|
11
|
+
import {printLeftOrCentered, TAGS as mmcrTAGS} from '@datagrok-libraries/bio/src/utils/cell-renderer';
|
|
12
|
+
import {getUpdatedWidth, MonomerPlacer, undefinedColor} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
|
|
12
13
|
import {
|
|
13
|
-
getPaletteByType,
|
|
14
14
|
monomerToShort,
|
|
15
15
|
MonomerToShortFunc,
|
|
16
16
|
NOTATION,
|
|
17
|
-
SplitterFunc,
|
|
18
17
|
TAGS as bioTAGS,
|
|
19
18
|
ALPHABET,
|
|
20
19
|
} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
21
|
-
import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
|
|
22
|
-
import {UnknownSeqPalettes} from '@datagrok-libraries/bio/src/unknown';
|
|
23
20
|
import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
|
|
24
|
-
import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
|
|
25
21
|
import {getSplitter} from '@datagrok-libraries/bio/src/utils/macromolecule/utils';
|
|
26
|
-
import {
|
|
27
|
-
import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
|
|
22
|
+
import {IMonomerLib, IMonomerLibBase} from '@datagrok-libraries/bio/src/types';
|
|
28
23
|
import {GapOriginals} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
|
|
29
24
|
import {ISeqMonomer} from '@datagrok-libraries/bio/src/helm/types';
|
|
30
25
|
import {execMonomerHoverLinks} from '@datagrok-libraries/bio/src/monomer-works/monomer-hover';
|
|
31
|
-
import {getGridCellColTemp} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
|
|
26
|
+
import {CellRendererBackBase, getGridCellColTemp} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
|
|
32
27
|
import {HelmTypes} from '@datagrok-libraries/bio/src/helm/consts';
|
|
33
28
|
import {MmcrTemps, rendererSettingsChangedState, tempTAGS} from '@datagrok-libraries/bio/src/utils/cell-renderer-consts';
|
|
29
|
+
import {getMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
34
30
|
import * as C from './constants';
|
|
35
31
|
|
|
36
|
-
import {_package
|
|
37
|
-
import {
|
|
32
|
+
import {_package} from '../package';
|
|
33
|
+
import {CellRendererWithMonomerLibBackBase} from './monomer-cell-renderer-base';
|
|
34
|
+
import {timeout} from 'rxjs/operators';
|
|
38
35
|
|
|
39
36
|
type TempType = { [tagName: string]: any };
|
|
40
37
|
|
|
@@ -78,54 +75,9 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
78
75
|
}
|
|
79
76
|
|
|
80
77
|
override onMouseMove(gridCell: DG.GridCell, e: MouseEvent): void {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// return;
|
|
85
|
-
|
|
86
|
-
const [_gridCol, tableCol, temp] =
|
|
87
|
-
getGridCellColTemp<string, MonomerPlacer>(gridCell);
|
|
88
|
-
const seqColTemp: MonomerPlacer = temp.rendererBack;
|
|
89
|
-
if (!seqColTemp) return; // Can do nothing without precalculated data
|
|
90
|
-
|
|
91
|
-
const gridCellBounds: DG.Rect = gridCell.bounds;
|
|
92
|
-
// const value: any = gridCell.cell.value;
|
|
93
|
-
//
|
|
94
|
-
// const maxLengthWords: number[] = seqColTemp.getCellMonomerLengths(gridCell.tableRowIndex!);
|
|
95
|
-
// const maxLengthWordsSum: number[] = new Array<number>(maxLengthWords.length).fill(0);
|
|
96
|
-
// for (let posI: number = 1; posI < maxLengthWords.length; posI++)
|
|
97
|
-
// maxLengthWordsSum[posI] = maxLengthWordsSum[posI - 1] + maxLengthWords[posI];
|
|
98
|
-
// const maxIndex = maxLengthWords.length;
|
|
99
|
-
const argsX = e.offsetX - gridCell.gridColumn.left + (gridCell.gridColumn.left - gridCellBounds.x);
|
|
100
|
-
const left: number | null = seqColTemp.getPosition(gridCell.tableRowIndex!, argsX, gridCellBounds.width);
|
|
101
|
-
|
|
102
|
-
const seqSS = SeqHandler.forColumn(tableCol)
|
|
103
|
-
.getSplitted(gridCell.tableRowIndex!);
|
|
104
|
-
if (left !== null && left < seqSS.length) {
|
|
105
|
-
const sh = SeqHandler.forColumn(tableCol);
|
|
106
|
-
const alphabet = sh.alphabet ?? ALPHABET.UN;
|
|
107
|
-
const seqMonomer = {
|
|
108
|
-
position: left,
|
|
109
|
-
biotype: alphabet === ALPHABET.RNA || alphabet === ALPHABET.DNA ? HelmTypes.NUCLEOTIDE : HelmTypes.AA,
|
|
110
|
-
symbol: seqSS.getCanonical(left),
|
|
111
|
-
} as ISeqMonomer;
|
|
112
|
-
const tooltipElements: HTMLElement[] = [];
|
|
113
|
-
let monomerDiv = seqColTemp._monomerStructureMap[seqMonomer.symbol];
|
|
114
|
-
if (!monomerDiv || true) {
|
|
115
|
-
monomerDiv = seqColTemp._monomerStructureMap[seqMonomer.symbol] = (() => {
|
|
116
|
-
const lib: IMonomerLib | null = getMonomerLib();
|
|
117
|
-
return lib ? lib.getTooltip(seqMonomer.biotype, seqMonomer.symbol) : ui.divText('Monomer library is not available');
|
|
118
|
-
})();
|
|
119
|
-
}
|
|
120
|
-
tooltipElements.push(monomerDiv);
|
|
121
|
-
ui.tooltip.show(ui.divV(tooltipElements), e.x + 16, e.y + 16);
|
|
122
|
-
|
|
123
|
-
execMonomerHoverLinks(gridCell, seqMonomer);
|
|
124
|
-
} else {
|
|
125
|
-
//
|
|
126
|
-
ui.tooltip.hide();
|
|
127
|
-
execMonomerHoverLinks(gridCell, null);
|
|
128
|
-
}
|
|
78
|
+
const [gridCol, tableCol, temp] = getGridCellColTemp<string, MonomerPlacer>(gridCell);
|
|
79
|
+
const back = temp.rendererBack;
|
|
80
|
+
back.onMouseMove(gridCell, e);
|
|
129
81
|
}
|
|
130
82
|
|
|
131
83
|
override onMouseLeave(gridCell: DG.GridCell, e: MouseEvent) {
|
|
@@ -200,6 +152,49 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
200
152
|
}
|
|
201
153
|
}
|
|
202
154
|
|
|
155
|
+
export class MacromoleculeDifferenceCellRendererBack extends CellRendererWithMonomerLibBackBase {
|
|
156
|
+
constructor(
|
|
157
|
+
gridCol: DG.GridColumn | null,
|
|
158
|
+
tableCol: DG.Column<string>,
|
|
159
|
+
) {
|
|
160
|
+
super(gridCol, tableCol);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
render(g: CanvasRenderingContext2D,
|
|
164
|
+
x: number, y: number, w: number, h: number, gridCell: DG.GridCell, cellStyle: DG.GridCellStyle
|
|
165
|
+
): void {
|
|
166
|
+
const dpr = window.devicePixelRatio;
|
|
167
|
+
const grid = gridCell.grid;
|
|
168
|
+
const cell = gridCell.cell;
|
|
169
|
+
const s: string = cell.value ?? '';
|
|
170
|
+
const separator = this.tableCol.tags[bioTAGS.separator];
|
|
171
|
+
const units: string = this.tableCol.meta.units!;
|
|
172
|
+
w = getUpdatedWidth(grid, g, x, w, dpr);
|
|
173
|
+
//TODO: can this be replaced/merged with splitSequence?
|
|
174
|
+
const [s1, s2] = s.split('#');
|
|
175
|
+
const splitter = getSplitter(units, separator);
|
|
176
|
+
const s1SS = splitter(s1);
|
|
177
|
+
const s2SS = splitter(s2);
|
|
178
|
+
const subParts1 = wu.count(0).take(s1SS.length).map((posIdx) => s1SS.getCanonical(posIdx)).toArray();
|
|
179
|
+
const subParts2 = wu.count(0).take(s2SS.length).map((posIdx) => s2SS.getCanonical(posIdx)).toArray();
|
|
180
|
+
const alphabet = this.tableCol.getTag(bioTAGS.alphabet);
|
|
181
|
+
const biotype = alphabet === ALPHABET.RNA || alphabet === ALPHABET.DNA ? HelmTypes.NUCLEOTIDE : HelmTypes.AA;
|
|
182
|
+
drawMoleculeDifferenceOnCanvas(g, x, y, w, h, subParts1, subParts2, biotype, this.monomerLib, undefined, undefined);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async awaitRendered(timeout: number = 10000, reason: string = `${timeout} timeout`): Promise<void> {
|
|
186
|
+
return Promise.resolve();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
static getOrCreate(gridCell: DG.GridCell): MacromoleculeDifferenceCellRendererBack {
|
|
190
|
+
const [gridCol, tableCol, temp] =
|
|
191
|
+
getGridCellColTemp<string, MacromoleculeDifferenceCellRendererBack>(gridCell);
|
|
192
|
+
|
|
193
|
+
let res: MacromoleculeDifferenceCellRendererBack = temp.rendererBack;
|
|
194
|
+
if (!res) res = temp.rendererBack = new MacromoleculeDifferenceCellRendererBack(gridCol, tableCol);
|
|
195
|
+
return res;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
203
198
|
|
|
204
199
|
export class MacromoleculeDifferenceCellRenderer extends DG.GridCellRenderer {
|
|
205
200
|
get name(): string { return 'MacromoleculeDifferenceCR'; }
|
|
@@ -222,25 +217,11 @@ export class MacromoleculeDifferenceCellRenderer extends DG.GridCellRenderer {
|
|
|
222
217
|
* @param {DG.GridCellStyle} _cellStyle Cell style.
|
|
223
218
|
* @memberof AlignedSequenceDifferenceCellRenderer
|
|
224
219
|
*/
|
|
225
|
-
render(
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
const cell = gridCell.cell;
|
|
231
|
-
const tableCol = gridCell.tableColumn as DG.Column<string>;
|
|
232
|
-
const s: string = cell.value ?? '';
|
|
233
|
-
const separator = tableCol.tags[bioTAGS.separator];
|
|
234
|
-
const units: string = tableCol.meta.units!;
|
|
235
|
-
w = getUpdatedWidth(grid, g, x, w, dpr);
|
|
236
|
-
//TODO: can this be replaced/merged with splitSequence?
|
|
237
|
-
const [s1, s2] = s.split('#');
|
|
238
|
-
const splitter = getSplitter(units, separator);
|
|
239
|
-
const s1SS = splitter(s1);
|
|
240
|
-
const s2SS = splitter(s2);
|
|
241
|
-
const subParts1 = wu.count(0).take(s1SS.length).map((posIdx) => s1SS.getCanonical(posIdx)).toArray();
|
|
242
|
-
const subParts2 = wu.count(0).take(s2SS.length).map((posIdx) => s2SS.getCanonical(posIdx)).toArray();
|
|
243
|
-
drawMoleculeDifferenceOnCanvas(g, x, y, w, h, subParts1, subParts2, units);
|
|
220
|
+
render(g: CanvasRenderingContext2D,
|
|
221
|
+
x: number, y: number, w: number, h: number, gridCell: DG.GridCell, _cellStyle: DG.GridCellStyle
|
|
222
|
+
): void {
|
|
223
|
+
const back = MacromoleculeDifferenceCellRendererBack.getOrCreate(gridCell);
|
|
224
|
+
back.render(g, x, y, w, h, gridCell, _cellStyle);
|
|
244
225
|
}
|
|
245
226
|
}
|
|
246
227
|
|
|
@@ -252,7 +233,8 @@ export function drawMoleculeDifferenceOnCanvas(
|
|
|
252
233
|
h: number,
|
|
253
234
|
subParts1: string[],
|
|
254
235
|
subParts2: string[],
|
|
255
|
-
|
|
236
|
+
biotype: HelmType,
|
|
237
|
+
monomerLib: IMonomerLibBase | null,
|
|
256
238
|
fullStringLength?: boolean,
|
|
257
239
|
molDifferences?: { [key: number]: HTMLCanvasElement },
|
|
258
240
|
): void {
|
|
@@ -273,39 +255,47 @@ export function drawMoleculeDifferenceOnCanvas(
|
|
|
273
255
|
const updatedY = Math.max(y, y + (h - 28) / 2);
|
|
274
256
|
|
|
275
257
|
g.save();
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
258
|
+
try {
|
|
259
|
+
g.beginPath();
|
|
260
|
+
g.rect(x, y, fullStringLength ? textWidth + subParts1.length * 4 : w, h);
|
|
261
|
+
g.clip();
|
|
262
|
+
g.font = '12px monospace';
|
|
263
|
+
g.textBaseline = 'top';
|
|
264
|
+
|
|
265
|
+
const vShift = 7;
|
|
266
|
+
for (let i = 0; i < subParts1.length; i++) {
|
|
267
|
+
const amino1 = subParts1[i];
|
|
268
|
+
const amino2 = subParts2[i];
|
|
269
|
+
|
|
270
|
+
let color1 = undefinedColor;
|
|
271
|
+
if (monomerLib) {
|
|
272
|
+
const wem1 = monomerLib.getWebEditorMonomer(biotype, amino1)!;
|
|
273
|
+
color1 = wem1.backgroundcolor!;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (amino1 != amino2) {
|
|
277
|
+
let color2 = undefinedColor;
|
|
278
|
+
if (monomerLib) {
|
|
279
|
+
const wem2 = monomerLib.getWebEditorMonomer(biotype, amino2)!;
|
|
280
|
+
color2 = wem2.backgroundcolor!;
|
|
281
|
+
}
|
|
282
|
+
const subX0 = printLeftOrCentered(g, amino1, updatedX, updatedY - vShift, w, h,
|
|
283
|
+
{color: color1, pivot: 0, left: true});
|
|
284
|
+
const subX1 = printLeftOrCentered(g, amino2, updatedX, updatedY + vShift, w, h,
|
|
285
|
+
{color: color2, pivot: 0, left: true});
|
|
286
|
+
updatedX = Math.max(subX1, subX0);
|
|
287
|
+
if (molDifferences)
|
|
288
|
+
molDifferences[i] = createDifferenceCanvas(amino1, amino2, color1, color2, updatedY, vShift, h);
|
|
289
|
+
} else {
|
|
290
|
+
//
|
|
291
|
+
updatedX = printLeftOrCentered(g, amino1, updatedX, updatedY, w, h,
|
|
292
|
+
{color: color1, pivot: 0, left: true, transparencyRate: 0.5});
|
|
293
|
+
}
|
|
294
|
+
updatedX += 4;
|
|
305
295
|
}
|
|
306
|
-
|
|
296
|
+
} finally {
|
|
297
|
+
g.restore();
|
|
307
298
|
}
|
|
308
|
-
g.restore();
|
|
309
299
|
}
|
|
310
300
|
|
|
311
301
|
interface IComparedSequences {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as grok from 'datagrok-api/grok';
|
|
2
|
+
import * as DG from 'datagrok-api/dg';
|
|
3
|
+
import * as ui from 'datagrok-api/ui';
|
|
4
|
+
|
|
5
|
+
import {getMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
6
|
+
import {CellRendererBackBase} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
|
|
7
|
+
import {IMonomerLibBase} from '@datagrok-libraries/bio/src/types/index';
|
|
8
|
+
|
|
9
|
+
import {_package} from '../package';
|
|
10
|
+
|
|
11
|
+
export abstract class CellRendererWithMonomerLibBackBase extends CellRendererBackBase<string> {
|
|
12
|
+
protected monomerLib: IMonomerLibBase | null = null;
|
|
13
|
+
|
|
14
|
+
constructor(
|
|
15
|
+
gridCol: DG.GridColumn | null,
|
|
16
|
+
tableCol: DG.Column<string>,
|
|
17
|
+
) {
|
|
18
|
+
super(gridCol, tableCol, _package.logger);
|
|
19
|
+
|
|
20
|
+
getMonomerLibHelper().then((libHelper) => {
|
|
21
|
+
this.monomerLib = libHelper.getMonomerLib();
|
|
22
|
+
this.dirty = true;
|
|
23
|
+
this.gridCol?.grid?.invalidate();
|
|
24
|
+
this.subs.push(this.monomerLib.onChanged.subscribe(() => {
|
|
25
|
+
this.dirty = true;
|
|
26
|
+
this.gridCol?.grid?.invalidate();
|
|
27
|
+
}));
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import * as grok from 'datagrok-api/grok';
|
|
2
1
|
import * as DG from 'datagrok-api/dg';
|
|
2
|
+
import {GridCell} from 'datagrok-api/dg';
|
|
3
3
|
import * as ui from 'datagrok-api/ui';
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import {ALPHABET, getPaletteByType, monomerToShort} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
7
|
-
import {TAGS as bioTAGS, } from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
|
|
4
|
+
import {ALPHABET, monomerToShort} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
5
|
+
import {GAP_SYMBOL, TAGS as bioTAGS,} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
|
|
8
6
|
import {MONOMER_RENDERER_TAGS} from '@datagrok-libraries/bio/src/utils/cell-renderer';
|
|
9
|
-
import {
|
|
7
|
+
import {getGridCellColTemp} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
|
|
10
8
|
|
|
9
|
+
import {CellRendererWithMonomerLibBackBase} from './monomer-cell-renderer-base';
|
|
11
10
|
import * as C from './constants';
|
|
12
|
-
import {
|
|
11
|
+
import {undefinedColor} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
|
|
12
|
+
import {HelmTypes} from '@datagrok-libraries/js-draw-lite/src/types/org';
|
|
13
13
|
|
|
14
14
|
const Tags = new class {
|
|
15
15
|
tooltipHandlerTemp = 'tooltip-handler.Monomer';
|
|
@@ -17,17 +17,51 @@ const Tags = new class {
|
|
|
17
17
|
|
|
18
18
|
const svgMolOptions = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
|
|
19
19
|
|
|
20
|
-
export class
|
|
21
|
-
|
|
20
|
+
export class MonomerCellRendererBack extends CellRendererWithMonomerLibBackBase {
|
|
21
|
+
constructor(gridCol: DG.GridColumn | null, tableCol: DG.Column) {
|
|
22
|
+
super(gridCol, tableCol);
|
|
23
|
+
}
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
render(g: CanvasRenderingContext2D,
|
|
26
|
+
x: number, y: number, w: number, h: number, gridCell: DG.GridCell, cellStyle: DG.GridCellStyle
|
|
27
|
+
): void {
|
|
28
|
+
g.save();
|
|
29
|
+
try {
|
|
30
|
+
if (gridCell.gridRow < 0) return;
|
|
31
|
+
const applyToBackground = gridCell.cell?.column && gridCell.cell.column.getTag(MONOMER_RENDERER_TAGS.applyToBackground) === 'true';
|
|
32
|
+
|
|
33
|
+
g.font = `12px monospace`;
|
|
34
|
+
g.textBaseline = 'middle';
|
|
35
|
+
g.textAlign = 'center';
|
|
36
|
+
|
|
37
|
+
const symbol: string = gridCell.cell.value;
|
|
38
|
+
if (!symbol || symbol == GAP_SYMBOL) return;
|
|
39
|
+
|
|
40
|
+
let color = undefinedColor;
|
|
41
|
+
if (this.monomerLib) {
|
|
42
|
+
const alphabet = this.tableCol.getTag(bioTAGS.alphabet);
|
|
43
|
+
const biotype = alphabet === ALPHABET.RNA || alphabet === ALPHABET.DNA ? HelmTypes.NUCLEOTIDE : HelmTypes.AA;
|
|
44
|
+
const wem = this.monomerLib.getWebEditorMonomer(biotype, symbol)!;
|
|
45
|
+
color = wem.backgroundcolor!;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
//cell width of monomer should dictate how many characters can be displayed
|
|
49
|
+
// for width 40, 6 characters can be displayed (0.15 is 6 / 40)
|
|
50
|
+
const maxChars = Math.max(2, Math.floor(w * 0.15));
|
|
51
|
+
g.fillStyle = color;
|
|
52
|
+
if (applyToBackground) {
|
|
53
|
+
g.fillRect(x, y, w, h);
|
|
54
|
+
g.fillStyle = DG.Color.toHtml(DG.Color.getContrastColor(DG.Color.fromHtml(color)));
|
|
55
|
+
}
|
|
56
|
+
g.fillText(monomerToShort(symbol, maxChars), x + (w / 2), y + (h / 2), w);
|
|
57
|
+
} finally {
|
|
58
|
+
g.restore();
|
|
59
|
+
}
|
|
26
60
|
}
|
|
27
61
|
|
|
28
|
-
|
|
62
|
+
override onMouseMove(gridCell: GridCell, e: MouseEvent) {
|
|
29
63
|
if (
|
|
30
|
-
gridCell.grid.dart != this.gridCol
|
|
64
|
+
gridCell.grid.dart != this.gridCol?.grid.dart || gridCell.gridColumn.dart != this.gridCol?.dart ||
|
|
31
65
|
!gridCell.tableColumn || !gridCell.isTableCell
|
|
32
66
|
) return false;
|
|
33
67
|
|
|
@@ -37,32 +71,33 @@ export class MonomerTooltipHandler {
|
|
|
37
71
|
const x1 = gridCell.bounds.right + canvasClientRect.left - 4;
|
|
38
72
|
const y1 = gridCell.bounds.bottom + canvasClientRect.top - 4;
|
|
39
73
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
ui.tooltip.show(ui.divText('Monomer library is not available.'), x1, y1);
|
|
74
|
+
if (monomerName == GAP_SYMBOL) {
|
|
75
|
+
ui.tooltip.show(ui.divText('gap'), x1, y1);
|
|
43
76
|
return true;
|
|
44
77
|
}
|
|
45
78
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// [ALPHABET.PT].includes(alphabet) ? HelmTypes.AA : HelmTypes.AA;
|
|
51
|
-
|
|
52
|
-
const monomerMol: string | null = mw.getCappedRotatedMonomer(polymerType, monomerName);
|
|
53
|
-
const nameDiv = ui.div(monomerName);
|
|
54
|
-
const molDiv = !monomerMol ? null :
|
|
55
|
-
grok.chem.svgMol(monomerMol, undefined, undefined, svgMolOptions);
|
|
79
|
+
if (!this.monomerLib) {
|
|
80
|
+
ui.tooltip.show(ui.divText('Monomer library is not available.'), x1, y1);
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
56
83
|
|
|
57
|
-
|
|
84
|
+
const biotype = alphabet === ALPHABET.RNA || alphabet === ALPHABET.DNA ? HelmTypes.NUCLEOTIDE : HelmTypes.AA;
|
|
85
|
+
const tooltipEl = this.monomerLib.getTooltip(biotype, monomerName);
|
|
86
|
+
ui.tooltip.show(tooltipEl, x1, y1);
|
|
58
87
|
|
|
59
88
|
return true; // To prevent default tooltip behaviour
|
|
60
89
|
}
|
|
61
90
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
91
|
+
override async awaitRendered(timeout: number = 10000, reason: string = `${timeout} timeout`): Promise<void> {
|
|
92
|
+
return Promise.resolve();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
static getOrCreate(gridCell: DG.GridCell): MonomerCellRendererBack {
|
|
96
|
+
const [gridCol, tableCol, temp] =
|
|
97
|
+
getGridCellColTemp<string, MonomerCellRendererBack>(gridCell);
|
|
98
|
+
|
|
99
|
+
let res: MonomerCellRendererBack = temp.rendererBack;
|
|
100
|
+
if (!res) res = temp.rendererBack = new MonomerCellRendererBack(gridCol, tableCol);
|
|
66
101
|
return res;
|
|
67
102
|
}
|
|
68
103
|
}
|
|
@@ -91,27 +126,12 @@ export class MonomerCellRenderer extends DG.GridCellRenderer {
|
|
|
91
126
|
g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, gridCell: DG.GridCell,
|
|
92
127
|
_cellStyle: DG.GridCellStyle
|
|
93
128
|
): void {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const palette = getPaletteByType(gridCell.cell.column.getTag(bioTAGS.alphabet));
|
|
103
|
-
const s: string = gridCell.cell.value;
|
|
104
|
-
if (!s)
|
|
105
|
-
return;
|
|
106
|
-
const color = palette.get(s);
|
|
107
|
-
//cell width of monomer should dictate how many characters can be displayed
|
|
108
|
-
// for width 40, 6 characters can be displayed (0.15 is 6 / 40)
|
|
109
|
-
const maxChars = Math.max(2, Math.floor(w * 0.15));
|
|
110
|
-
g.fillStyle = color;
|
|
111
|
-
if (applyToBackground) {
|
|
112
|
-
g.fillRect(x, y, w, h);
|
|
113
|
-
g.fillStyle = DG.Color.toHtml(DG.Color.getContrastColor(DG.Color.fromHtml(color)));
|
|
114
|
-
}
|
|
115
|
-
g.fillText(monomerToShort(s, maxChars), x + (w / 2), y + (h / 2), w);
|
|
129
|
+
const back = MonomerCellRendererBack.getOrCreate(gridCell);
|
|
130
|
+
back.render(g, x, y, w, h, gridCell, _cellStyle);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
onMouseMove(gridCell: GridCell, e: MouseEvent) {
|
|
134
|
+
const back = MonomerCellRendererBack.getOrCreate(gridCell);
|
|
135
|
+
back.onMouseMove(gridCell, e);
|
|
116
136
|
}
|
|
117
137
|
}
|
|
@@ -95,7 +95,7 @@ export class MonomerLibManager implements IMonomerLibHelper {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
assignDuplicatePreferances(settings: UserLibSettings) {
|
|
98
|
-
this._monomerLib.
|
|
98
|
+
this._monomerLib.assignDuplicatePreferences(settings);
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
/** Instance promise of {@link getFileManager} */
|