@datagrok/bio 2.15.7 → 2.15.9

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.
@@ -24,8 +24,8 @@
24
24
  "colors": {
25
25
  "default": {
26
26
  "line": "#202020",
27
- "text": "#202020",
28
- "background": "#60FF40"
27
+ "text": "#20C010",
28
+ "background": "#F0F0F0"
29
29
  }
30
30
  }
31
31
  },
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.9",
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,12 +37,12 @@
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.5",
41
41
  "@datagrok-libraries/chem-meta": "^1.2.7",
42
- "@datagrok-libraries/math": "^1.2.0",
43
- "@datagrok-libraries/ml": "^6.7.1",
44
- "@datagrok-libraries/tutorials": "^1.4.2",
45
- "@datagrok-libraries/utils": "^4.3.4",
42
+ "@datagrok-libraries/math": "^1.2.1",
43
+ "@datagrok-libraries/ml": "^6.7.3",
44
+ "@datagrok-libraries/tutorials": "^1.4.3",
45
+ "@datagrok-libraries/utils": "^4.3.6",
46
46
  "@webgpu/types": "^0.1.40",
47
47
  "ajv": "^8.12.0",
48
48
  "ajv-errors": "^3.0.0",
@@ -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');
@@ -5,6 +5,7 @@ import * as ui from 'datagrok-api/ui';
5
5
  import {getGridCellColTemp, CellRendererBackBase} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
6
6
  import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
7
7
  import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
8
+ import {GridCell} from 'datagrok-api/dg';
8
9
 
9
10
  export class MacromoleculeCustomCellRenderer extends DG.GridCellRenderer {
10
11
  get name(): string { return 'sequence'; }
@@ -24,9 +25,16 @@ export class MacromoleculeCustomCellRenderer extends DG.GridCellRenderer {
24
25
  return back;
25
26
  }
26
27
 
27
- override onMouseLeave(gridCell: DG.GridCell, e: MouseEvent) {
28
+ override onMouseEnter(gridCell: GridCell, e: MouseEvent) {
28
29
  const back = this.getRendererBack(gridCell);
29
- back.onMouseLeave(gridCell, e);
30
+ back.onMouseEnter(gridCell, e);
31
+ }
32
+
33
+ override onMouseLeave(gridCell: DG.GridCell, e: MouseEvent) {
34
+ // TODO: We get gridCell from another column here, so we can not get back object from the column rendered.
35
+ ui.tooltip.hide();
36
+ // const back = this.getRendererBack(gridCell);
37
+ // back.onMouseLeave(gridCell, e);
30
38
  }
31
39
 
32
40
  override onMouseMove(gridCell: DG.GridCell, e: MouseEvent) {
@@ -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) {
@@ -150,10 +102,11 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
150
102
  ): void {
151
103
  const logPrefix: string = 'MacromoleculeSequenceCellRenderer.render()';
152
104
 
153
- const [gridCol, tableCol, _temp] =
105
+ const [gridCol, tableCol, temp] =
154
106
  getGridCellColTemp<string, MonomerPlacer>(gridCell);
155
107
  if (!tableCol) return;
156
108
  const tableColTemp: TempType = tableCol.temp;
109
+ const sh = SeqHandler.forColumn(tableCol);
157
110
 
158
111
  let gapLength = 0;
159
112
  const msaGapLength = 8;
@@ -169,37 +122,65 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
169
122
  maxLengthOfMonomer = !isNaN(v) && v ? v : 50;
170
123
  }
171
124
 
172
- const [_gc, _tc, temp] =
173
- getGridCellColTemp<string, MonomerPlacer>(gridCell);
174
125
  let seqColTemp: MonomerPlacer = temp.rendererBack;
175
126
  if (!seqColTemp) {
176
127
  seqColTemp = temp.rendererBack = new MonomerPlacer(gridCol, tableCol, _package.logger, maxLengthOfMonomer,
177
128
  () => {
178
- const sh = SeqHandler.forColumn(tableCol);
179
129
  return {
180
- seqHandler: sh,
181
130
  monomerCharWidth: 7, separatorWidth: !sh.isMsa() ? gapLength : msaGapLength,
182
131
  monomerToShort: monomerToShortFunction,
183
132
  };
184
133
  });
185
- }
186
-
187
- if (
188
- tableCol.temp[MmcrTemps.rendererSettingsChanged] === rendererSettingsChangedState.true ||
189
- seqColTemp.monomerLengthLimit != maxLengthOfMonomer
190
- ) {
191
- gapLength = tableColTemp[MmcrTemps.gapLength] as number ?? gapLength;
192
- // this event means that the mm renderer settings have changed,
193
- // particularly monomer representation and max width.
194
- seqColTemp.setMonomerLengthLimit(maxLengthOfMonomer);
195
- seqColTemp.setSeparatorWidth(seqColTemp.isMsa() ? msaGapLength : gapLength);
196
- tableCol.temp[MmcrTemps.rendererSettingsChanged] = rendererSettingsChangedState.false;
134
+ tableCol.temp[MmcrTemps.rendererSettingsChanged] === rendererSettingsChangedState.true;
197
135
  }
198
136
 
199
137
  seqColTemp.render(g, x, y, w, h, gridCell, _cellStyle);
200
138
  }
201
139
  }
202
140
 
141
+ export class MacromoleculeDifferenceCellRendererBack extends CellRendererWithMonomerLibBackBase {
142
+ constructor(
143
+ gridCol: DG.GridColumn | null,
144
+ tableCol: DG.Column<string>,
145
+ ) {
146
+ super(gridCol, tableCol);
147
+ }
148
+
149
+ render(g: CanvasRenderingContext2D,
150
+ x: number, y: number, w: number, h: number, gridCell: DG.GridCell, cellStyle: DG.GridCellStyle
151
+ ): void {
152
+ const dpr = window.devicePixelRatio;
153
+ const grid = gridCell.grid;
154
+ const cell = gridCell.cell;
155
+ const s: string = cell.value ?? '';
156
+ const separator = this.tableCol.tags[bioTAGS.separator];
157
+ const units: string = this.tableCol.meta.units!;
158
+ w = getUpdatedWidth(grid, g, x, w, dpr);
159
+ //TODO: can this be replaced/merged with splitSequence?
160
+ const [s1, s2] = s.split('#');
161
+ const splitter = getSplitter(units, separator);
162
+ const s1SS = splitter(s1);
163
+ const s2SS = splitter(s2);
164
+ const subParts1 = wu.count(0).take(s1SS.length).map((posIdx) => s1SS.getCanonical(posIdx)).toArray();
165
+ const subParts2 = wu.count(0).take(s2SS.length).map((posIdx) => s2SS.getCanonical(posIdx)).toArray();
166
+ const alphabet = this.tableCol.getTag(bioTAGS.alphabet);
167
+ const biotype = alphabet === ALPHABET.RNA || alphabet === ALPHABET.DNA ? HelmTypes.NUCLEOTIDE : HelmTypes.AA;
168
+ drawMoleculeDifferenceOnCanvas(g, x, y, w, h, subParts1, subParts2, biotype, this.monomerLib, undefined, undefined);
169
+ }
170
+
171
+ async awaitRendered(timeout: number = 10000, reason: string = `${timeout} timeout`): Promise<void> {
172
+ return Promise.resolve();
173
+ }
174
+
175
+ static getOrCreate(gridCell: DG.GridCell): MacromoleculeDifferenceCellRendererBack {
176
+ const [gridCol, tableCol, temp] =
177
+ getGridCellColTemp<string, MacromoleculeDifferenceCellRendererBack>(gridCell);
178
+
179
+ let res: MacromoleculeDifferenceCellRendererBack = temp.rendererBack;
180
+ if (!res) res = temp.rendererBack = new MacromoleculeDifferenceCellRendererBack(gridCol, tableCol);
181
+ return res;
182
+ }
183
+ }
203
184
 
204
185
  export class MacromoleculeDifferenceCellRenderer extends DG.GridCellRenderer {
205
186
  get name(): string { return 'MacromoleculeDifferenceCR'; }
@@ -222,25 +203,11 @@ export class MacromoleculeDifferenceCellRenderer extends DG.GridCellRenderer {
222
203
  * @param {DG.GridCellStyle} _cellStyle Cell style.
223
204
  * @memberof AlignedSequenceDifferenceCellRenderer
224
205
  */
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);
206
+ render(g: CanvasRenderingContext2D,
207
+ x: number, y: number, w: number, h: number, gridCell: DG.GridCell, _cellStyle: DG.GridCellStyle
208
+ ): void {
209
+ const back = MacromoleculeDifferenceCellRendererBack.getOrCreate(gridCell);
210
+ back.render(g, x, y, w, h, gridCell, _cellStyle);
244
211
  }
245
212
  }
246
213
 
@@ -252,7 +219,8 @@ export function drawMoleculeDifferenceOnCanvas(
252
219
  h: number,
253
220
  subParts1: string[],
254
221
  subParts2: string[],
255
- units: string,
222
+ biotype: HelmType,
223
+ monomerLib: IMonomerLibBase | null,
256
224
  fullStringLength?: boolean,
257
225
  molDifferences?: { [key: number]: HTMLCanvasElement },
258
226
  ): void {
@@ -273,39 +241,45 @@ export function drawMoleculeDifferenceOnCanvas(
273
241
  const updatedY = Math.max(y, y + (h - 28) / 2);
274
242
 
275
243
  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});
244
+ try {
245
+ g.beginPath();
246
+ g.rect(x, y, fullStringLength ? textWidth + subParts1.length * 4 : w, h);
247
+ g.clip();
248
+ g.font = '12px monospace';
249
+ g.textBaseline = 'top';
250
+
251
+ const vShift = 7;
252
+ for (let i = 0; i < subParts1.length; i++) {
253
+ const amino1 = subParts1[i];
254
+ const amino2 = subParts2[i];
255
+
256
+ let color1 = undefinedColor;
257
+ if (monomerLib) {
258
+ color1 = monomerLib.getMonomerTextColor(biotype, amino1);
259
+ }
260
+
261
+ if (amino1 != amino2) {
262
+ let color2 = undefinedColor;
263
+ if (monomerLib) {
264
+ color2 = monomerLib.getMonomerTextColor(biotype, amino2);
265
+ }
266
+ const subX0 = printLeftOrCentered(g, amino1, updatedX, updatedY - vShift, w, h,
267
+ {color: color1, pivot: 0, left: true});
268
+ const subX1 = printLeftOrCentered(g, amino2, updatedX, updatedY + vShift, w, h,
269
+ {color: color2, pivot: 0, left: true});
270
+ updatedX = Math.max(subX1, subX0);
271
+ if (molDifferences)
272
+ molDifferences[i] = createDifferenceCanvas(amino1, amino2, color1, color2, updatedY, vShift, h);
273
+ } else {
274
+ //
275
+ updatedX = printLeftOrCentered(g, amino1, updatedX, updatedY, w, h,
276
+ {color: color1, pivot: 0, left: true, transparencyRate: 0.5});
277
+ }
278
+ updatedX += 4;
305
279
  }
306
- updatedX += 4;
280
+ } finally {
281
+ g.restore();
307
282
  }
308
- g.restore();
309
283
  }
310
284
 
311
285
  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
+ }