@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/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "Aleksandr Tanas",
6
6
  "email": "atanas@datagrok.ai"
7
7
  },
8
- "version": "2.15.7",
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.3",
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.1",
63
+ "@datagrok/chem": "^1.12.3",
64
64
  "@datagrok/dendrogram": "^1.2.33",
65
- "@datagrok/helm": "^2.5.2",
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 subParts1 = sh.getSplitted(params.points[0]); // splitter(sequencesArray[0], {uh, rowIdx: -1});
110
- const subParts2 = sh.getSplitted(params.points[1]); // splitter(sequencesArray[1], {uh, rowIdx: -1});
111
- const canvas = createDifferenceCanvas(subParts1, subParts2, sh.units, molDifferences);
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
- units: string,
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
- units, true, molDifferences);
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.units, molDifferences);
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 {printLeftOrCentered, DrawStyle, TAGS as mmcrTAGS} from '@datagrok-libraries/bio/src/utils/cell-renderer';
11
- import {getUpdatedWidth, MonomerPlacer} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
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 {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
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, getMonomerLib} from '../package';
37
- import {max} from 'rxjs/operators';
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
- if (gridCell.tableRowIndex == null) return;
82
-
83
- // if (gridCell.cell.column.getTag(bioTAGS.aligned) !== ALIGNMENT.SEQ_MSA)
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
- g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, gridCell: DG.GridCell,
227
- _cellStyle: DG.GridCellStyle): void {
228
- const dpr = window.devicePixelRatio;
229
- const grid = gridCell.grid;
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
- units: string,
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
- g.beginPath();
277
- g.rect(x, y, fullStringLength ? textWidth + subParts1.length * 4 : w, h);
278
- g.clip();
279
- g.font = '12px monospace';
280
- g.textBaseline = 'top';
281
-
282
- let palette: SeqPalette = UnknownSeqPalettes.Color;
283
- if (units !== 'HELM')
284
- palette = getPaletteByType(units.substring(units.length - 2));
285
-
286
- const vShift = 7;
287
- for (let i = 0; i < subParts1.length; i++) {
288
- const amino1 = subParts1[i];
289
- const amino2 = subParts2[i];
290
- const color1 = palette.get(amino1);
291
-
292
- if (amino1 != amino2) {
293
- const color2 = palette.get(amino2);
294
- const subX0 = printLeftOrCentered(g, amino1, updatedX, updatedY - vShift, w, h,
295
- {color: color1, pivot: 0, left: true});
296
- const subX1 = printLeftOrCentered(g, amino2, updatedX, updatedY + vShift, w, h,
297
- {color: color2, pivot: 0, left: true});
298
- updatedX = Math.max(subX1, subX0);
299
- if (molDifferences)
300
- molDifferences[i] = createDifferenceCanvas(amino1, amino2, color1, color2, updatedY, vShift, h);
301
- } else {
302
- //
303
- updatedX = printLeftOrCentered(g, amino1, updatedX, updatedY, w, h,
304
- {color: color1, pivot: 0, left: true, transparencyRate: 0.5});
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
- updatedX += 4;
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 {PolymerType} from '@datagrok-libraries/bio/src/helm/types';
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 {MonomerWorks} from '@datagrok-libraries/bio/src/monomer-works/monomer-works';
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 {getMonomerLib} from '../package';
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 MonomerTooltipHandler {
21
- private readonly gridCol: DG.GridColumn;
20
+ export class MonomerCellRendererBack extends CellRendererWithMonomerLibBackBase {
21
+ constructor(gridCol: DG.GridColumn | null, tableCol: DG.Column) {
22
+ super(gridCol, tableCol);
23
+ }
22
24
 
23
- constructor(gridCol: DG.GridColumn) {
24
- this.gridCol = gridCol;
25
- this.gridCol.grid.onCellTooltip(this.onCellTooltip.bind(this));
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
- private onCellTooltip(gridCell: DG.GridCell, x: number, y: number): any {
62
+ override onMouseMove(gridCell: GridCell, e: MouseEvent) {
29
63
  if (
30
- gridCell.grid.dart != this.gridCol.grid.dart || gridCell.gridColumn.dart != this.gridCol.dart ||
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
- const monomerLib = getMonomerLib();
41
- if (!monomerLib) {
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
- const mw = new MonomerWorks(monomerLib);
47
- const polymerType: PolymerType = (alphabet === ALPHABET.DNA || alphabet === ALPHABET.RNA) ? 'RNA' :
48
- alphabet === ALPHABET.PT ? 'PEPTIDE' : 'PEPTIDE';
49
- // const biotype: HelmType = [ALPHABET.RNA, ALPHABET.DNA].includes(alphabet) ? HelmTypes.NUCLEOTIDE :
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
- ui.tooltip.show(ui.divV([nameDiv, ...(molDiv ? [molDiv] : [])]), x1, y1);
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
- public static getOrCreate(gridCol: DG.GridColumn): MonomerTooltipHandler {
63
- let res = gridCol.temp[Tags.tooltipHandlerTemp];
64
- if (!res)
65
- res = gridCol.temp[Tags.tooltipHandlerTemp] = new MonomerTooltipHandler(gridCol);
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
- if (gridCell.gridRow < 0) return;
95
- MonomerTooltipHandler.getOrCreate(gridCell.gridColumn);
96
- const applyToBackground = gridCell.cell?.column && gridCell.cell.column.getTag(MONOMER_RENDERER_TAGS.applyToBackground) === 'true';
97
-
98
- g.font = `12px monospace`;
99
- g.textBaseline = 'middle';
100
- g.textAlign = 'center';
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.assignDuplicatePreferances(settings);
98
+ this._monomerLib.assignDuplicatePreferences(settings);
99
99
  }
100
100
 
101
101
  /** Instance promise of {@link getFileManager} */