@datagrok/bio 2.0.13 → 2.0.16

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.
@@ -11,7 +11,6 @@ import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
11
11
 
12
12
  const undefinedColor = 'rgb(100,100,100)';
13
13
  const monomerToShortFunction: (amino: string, maxLengthOfMonomer: number) => string = WebLogo.monomerToShort;
14
- const gapRenderer = 5;
15
14
 
16
15
 
17
16
  function getPaletteByType(paletteType: string): SeqPalette {
@@ -61,10 +60,15 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
61
60
 
62
61
  get defaultWidth(): number { return 230; }
63
62
 
63
+ onClick(gridCell: DG.GridCell, e: MouseEvent): void {
64
+ gridCell.cell.column.temp['current-word'] = gridCell.cell.value;
65
+ gridCell.grid.invalidate();
66
+ }
67
+
64
68
  onMouseMove(gridCell: DG.GridCell, e: MouseEvent): void {
65
- if (gridCell.cell.column.getTag(UnitsHandler.TAGS.aligned) !== 'SEQ.MSA') {
69
+ if (gridCell.cell.column.getTag(UnitsHandler.TAGS.aligned) !== 'SEQ.MSA')
66
70
  return;
67
- }
71
+
68
72
  const maxLengthWordsSum = gridCell.cell.column.temp['bio-sum-maxLengthWords'];
69
73
  const maxIndex = gridCell.cell.column.temp['bio-maxIndex'];
70
74
  const argsX = e.offsetX - gridCell.gridColumn.left + (gridCell.gridColumn.left - gridCell.bounds.x);
@@ -84,16 +88,16 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
84
88
  } else if (argsX > maxLengthWordsSum[mid + 1]) {
85
89
  left = mid + 1;
86
90
  }
87
- if (left == right) {
91
+ if (left == right)
88
92
  found = true;
89
- }
90
93
  }
91
94
  }
92
95
  left = (argsX >= maxLengthWordsSum[left]) ? left + 1 : left;
93
96
  const separator = gridCell.cell.column.getTag('separator') ?? '';
94
97
  const splitterFunc: SplitterFunc = WebLogo.getSplitter('separator', separator);
95
98
  const subParts: string[] = splitterFunc(gridCell.cell.value);
96
- (((subParts[left]?.length ?? 0) > 0)) ? ui.tooltip.show(ui.div(subParts[left]), e.x + 16, e.y + 16) : ui.tooltip.hide();
99
+ (((subParts[left]?.length ?? 0) > 0)) ?
100
+ ui.tooltip.show(ui.div(subParts[left]), e.x + 16, e.y + 16) : ui.tooltip.hide();
97
101
  }
98
102
 
99
103
  /**
@@ -131,45 +135,52 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
131
135
 
132
136
  const separator = gridCell.cell.column.getTag('separator') ?? '';
133
137
  const splitLimit = gridCell.bounds.width / 5;
134
- const splitterFunc: SplitterFunc = WebLogo.getSplitter(units, separator, gridCell.bounds.width / 5);
138
+ const splitterFunc: SplitterFunc = WebLogo.getSplitter(units, separator, splitLimit);
139
+ const referenceSequence: string[] = splitterFunc(((gridCell.cell.column?.temp['reference-sequence'] != null) && (gridCell.cell.column?.temp['reference-sequence'] != ''))
140
+ ? gridCell.cell.column.temp['reference-sequence'] : gridCell.cell.column.temp['current-word'] ?? '');
141
+ const monomerWidth = (gridCell.cell.column?.temp['monomer-width'] != null) ? gridCell.cell.column.temp['monomer-width'] : 'short';
142
+ let gapRenderer = 5;
135
143
 
144
+ let maxIndex = 0;
136
145
 
137
- const maxLengthOfMonomer = 8;
146
+ let maxLengthOfMonomer = 8;
147
+
148
+ if (monomerWidth === 'short') {
149
+ gapRenderer = 12;
150
+ maxLengthOfMonomer = 1;
151
+ }
138
152
 
139
153
  let maxLengthWords: any = {};
140
154
  if (gridCell.cell.column.getTag('.calculatedCellRender') !== splitLimit.toString()) {
141
155
  let samples = 0;
142
156
  while (samples < Math.min(gridCell.cell.column.length, 100)) {
143
- let column = gridCell.cell.column.get(samples);
144
- let subParts: string[] = splitterFunc(column);
157
+ const column = gridCell.cell.column.get(samples);
158
+ const subParts: string[] = splitterFunc(column);
145
159
  subParts.forEach((amino, index) => {
146
- let textSize = monomerToShortFunction(amino, maxLengthOfMonomer).length * 7 + gapRenderer;
147
- if (textSize > (maxLengthWords[index] ?? 0)) {
160
+ const textSize = monomerToShortFunction(amino, maxLengthOfMonomer).length * 7 + gapRenderer;
161
+ if (textSize > (maxLengthWords[index] ?? 0))
148
162
  maxLengthWords[index] = textSize;
149
- }
150
- if (index > (maxLengthWords['bio-maxIndex'] ?? 0)) {
151
- maxLengthWords['bio-maxIndex'] = index;
163
+ if (index > maxIndex) {
164
+ maxIndex = index;
152
165
  }
153
166
  });
154
167
  samples += 1;
155
168
  }
156
169
  let minLength = 3 * 7;
157
- for (let i = 0; i <= maxLengthWords['bio-maxIndex']; i++) {
170
+ for (let i = 0; i <= maxIndex; i++) {
158
171
  if (maxLengthWords[i] < minLength) {
159
172
  maxLengthWords[i] = minLength;
160
173
  }
174
+ const maxLengthWordSum: any = {};
175
+ maxLengthWordSum[0] = maxLengthWords[0];
176
+ for (let i = 1; i <= maxIndex; i++) {
177
+ maxLengthWordSum[i] = maxLengthWordSum[i - 1] + maxLengthWords[i];
178
+ }
179
+ gridCell.cell.column.temp['bio-sum-maxLengthWords'] = maxLengthWordSum;
180
+ gridCell.cell.column.temp['bio-maxIndex'] = maxIndex;
181
+ gridCell.cell.column.temp['bio-maxLengthWords'] = maxLengthWords;
182
+ gridCell.cell.column.setTag('.calculatedCellRender', splitLimit.toString());
161
183
  }
162
- let maxLengthWordSum: any = {};
163
- maxLengthWordSum[0] = maxLengthWords[0];
164
- for (let i = 1; i <= maxLengthWords['bio-maxIndex']; i++) {
165
- maxLengthWordSum[i] = maxLengthWordSum[i - 1] + maxLengthWords[i];
166
- }
167
- gridCell.cell.column.temp = {
168
- 'bio-sum-maxLengthWords': maxLengthWordSum,
169
- 'bio-maxIndex': maxLengthWords['bio-maxIndex'],
170
- 'bio-maxLengthWords': maxLengthWords
171
- };
172
- gridCell.cell.column.setTag('.calculatedCellRender', splitLimit.toString());
173
184
  } else {
174
185
  maxLengthWords = gridCell.cell.column.temp['bio-maxLengthWords'];
175
186
  }
@@ -178,16 +189,15 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
178
189
  let x1 = x;
179
190
  let color = undefinedColor;
180
191
  let drawStyle = DrawStyle.classic;
181
- if (gridCell.cell.column.getTag('aligned').includes('MSA') && gridCell.cell.column.getTag('units') === 'separator') {
192
+ if (gridCell.cell.column.getTag('aligned').includes('MSA') && gridCell.cell.column.getTag('units') === 'separator')
182
193
  drawStyle = DrawStyle.MSA;
183
- }
194
+
184
195
  subParts.every((amino, index) => {
185
196
  color = palette.get(amino);
186
197
  g.fillStyle = undefinedColor;
187
198
  let last = index === subParts.length - 1;
188
- x1 = printLeftOrCentered(x1, y, w, h, g, monomerToShortFunction(amino, maxLengthOfMonomer), color, 0, true, 1.0, separator, last, drawStyle, maxLengthWords, index, gridCell);
199
+ x1 = printLeftOrCentered(x1, y, w, h, g, amino, color, 0, true, 1.0, separator, last, drawStyle, maxLengthWords, index, gridCell, referenceSequence, maxLengthOfMonomer);
189
200
  return x1 - minDistanceRenderer - gridCell.gridColumn.left + (gridCell.gridColumn.left - gridCell.bounds.x) <= gridCell.bounds.width;
190
-
191
201
  });
192
202
 
193
203
  g.restore();
@@ -196,13 +206,13 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
196
206
  }
197
207
 
198
208
  export class MonomerCellRenderer extends DG.GridCellRenderer {
199
- get name(): string {return C.SEM_TYPES.MONOMER;}
209
+ get name(): string { return C.SEM_TYPES.MONOMER; }
200
210
 
201
- get cellType(): string {return C.SEM_TYPES.MONOMER;}
211
+ get cellType(): string { return C.SEM_TYPES.MONOMER; }
202
212
 
203
- get defaultHeight(): number {return 15;}
213
+ get defaultHeight(): number { return 15; }
204
214
 
205
- get defaultWidth(): number {return 30;}
215
+ get defaultWidth(): number { return 30; }
206
216
 
207
217
  /**
208
218
  * Cell renderer function.
@@ -213,7 +223,7 @@ export class MonomerCellRenderer extends DG.GridCellRenderer {
213
223
  * @param {number} w width of the cell.
214
224
  * @param {number} h height of the cell.
215
225
  * @param {DG.GridCell} gridCell Grid cell.
216
- * @param {DG.GridCellStyle} cellStyle Cell style.
226
+ * @param {DG.GridCellStyle} _cellStyle Cell style.
217
227
  */
218
228
  render(
219
229
  g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, gridCell: DG.GridCell,
@@ -249,7 +259,7 @@ export class MacromoleculeDifferenceCellRenderer extends DG.GridCellRenderer {
249
259
  * @param {number} w width of the cell.
250
260
  * @param {number} h height of the cell.
251
261
  * @param {DG.GridCell} gridCell Grid cell.
252
- * @param {DG.GridCellStyle} cellStyle Cell style.
262
+ * @param {DG.GridCellStyle} _cellStyle Cell style.
253
263
  * @memberof AlignedSequenceDifferenceCellRenderer
254
264
  */
255
265
  render(
@@ -261,7 +271,12 @@ export class MacromoleculeDifferenceCellRenderer extends DG.GridCellRenderer {
261
271
  const separator = gridCell.tableColumn!.tags[C.TAGS.SEPARATOR];
262
272
  const units: string = gridCell.tableColumn!.tags[DG.TAGS.UNITS];
263
273
  w = getUpdatedWidth(grid, g, x, w);
264
- drawMoleculeDifferenceOnCanvas(g, x, y, w, h, s, units, separator);
274
+ //TODO: can this be replaced/merged with splitSequence?
275
+ const [s1, s2] = s.split('#');
276
+ const splitter = WebLogo.getSplitter(units, separator);
277
+ const subParts1 = splitter(s1);
278
+ const subParts2 = splitter(s2);
279
+ drawMoleculeDifferenceOnCanvas(g, x, y, w, h, subParts1, subParts2, units, separator);
265
280
  }
266
281
  }
267
282
 
@@ -271,17 +286,17 @@ export function drawMoleculeDifferenceOnCanvas(
271
286
  y: number,
272
287
  w: number,
273
288
  h: number,
274
- s: string,
289
+ subParts1: string [],
290
+ subParts2: string [],
275
291
  units: string,
276
- separator: string,
277
292
  fullStringLength?: boolean,
278
- molDifferences?:{[key: number]: HTMLCanvasElement}) {
293
+ molDifferences?: { [key: number]: HTMLCanvasElement }) {
279
294
 
280
- //TODO: can this be replaced/merged with splitSequence?
281
- const [s1, s2] = s.split('#');
282
- const splitter = WebLogo.getSplitter(units, separator);
283
- const subParts1 = splitter(s1);
284
- const subParts2 = splitter(s2);
295
+ if (subParts1.length !== subParts2.length) {
296
+ const emptyMonomersArray = new Array<string>(Math.abs(subParts1.length - subParts2.length)).fill('');
297
+ subParts1.length > subParts2.length ?
298
+ subParts2 = subParts2.concat(emptyMonomersArray) : subParts1 = subParts1.concat(emptyMonomersArray);
299
+ }
285
300
  const textSize1 = g.measureText(processSequence(subParts1).join(''));
286
301
  const textSize2 = g.measureText(processSequence(subParts2).join(''));
287
302
  const textWidth = Math.max(textSize1.width, textSize2.width);
@@ -315,35 +330,33 @@ export function drawMoleculeDifferenceOnCanvas(
315
330
  const subX0 = printLeftOrCentered(updatedX, updatedY - vShift, w, h, g, amino1, color1, 0, true);
316
331
  const subX1 = printLeftOrCentered(updatedX, updatedY + vShift, w, h, g, amino2, color2, 0, true);
317
332
  updatedX = Math.max(subX1, subX0);
318
- if (molDifferences) {
333
+ if (molDifferences)
319
334
  molDifferences[i] = createDifferenceCanvas(amino1, amino2, color1, color2, updatedY, vShift, h);
320
- }
321
- } else
322
- updatedX = printLeftOrCentered(updatedX, updatedY, w, h, g, amino1, color1, 0, true, 0.5);
335
+ } else { updatedX = printLeftOrCentered(updatedX, updatedY, w, h, g, amino1, color1, 0, true, 0.5); }
323
336
  updatedX += 4;
324
337
  }
325
338
  g.restore();
326
339
  }
327
340
 
328
341
  function createDifferenceCanvas(
329
- amino1: string,
330
- amino2: string,
331
- color1: string,
332
- color2: string,
342
+ amino1: string,
343
+ amino2: string,
344
+ color1: string,
345
+ color2: string,
333
346
  y: number,
334
- shift: number,
347
+ shift: number,
335
348
  h: number): HTMLCanvasElement {
336
- const canvas = document.createElement('canvas');
337
- const context = canvas.getContext('2d')!;
338
- context.font = '12px monospace';
339
- const width1 = context.measureText(processSequence([amino1]).join('')).width;
340
- const width2 = context.measureText(processSequence([amino2]).join('')).width;
341
- const width = Math.max(width1, width2);
342
- canvas.height = h;
343
- canvas.width = width + 4;
344
- context.font = '12px monospace';
345
- context.textBaseline = 'top';
346
- printLeftOrCentered(0, y - shift, width, h, context, amino1, color1, 0, true);
347
- printLeftOrCentered(0, y + shift, width, h, context, amino2, color2, 0, true);
348
- return canvas;
349
+ const canvas = document.createElement('canvas');
350
+ const context = canvas.getContext('2d')!;
351
+ context.font = '12px monospace';
352
+ const width1 = context.measureText(processSequence([amino1]).join('')).width;
353
+ const width2 = context.measureText(processSequence([amino2]).join('')).width;
354
+ const width = Math.max(width1, width2);
355
+ canvas.height = h;
356
+ canvas.width = width + 4;
357
+ context.font = '12px monospace';
358
+ context.textBaseline = 'top';
359
+ printLeftOrCentered(0, y - shift, width, h, context, amino1, color1, 0, true);
360
+ printLeftOrCentered(0, y + shift, width, h, context, amino2, color2, 0, true);
361
+ return canvas;
349
362
  }
@@ -0,0 +1,4 @@
1
+ export function updateDivInnerHTML(div: HTMLElement, content: string | Node): void {
2
+ div.innerHTML = '';
3
+ div.append(content);
4
+ }
@@ -3,6 +3,7 @@ import * as grok from 'datagrok-api/grok';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
 
5
5
  import {VdRegionType, VdRegion} from '@datagrok-libraries/bio/src/vd-regions';
6
+ import {IVdRegionsViewer} from '@datagrok-libraries/bio/src/viewers/vd-regions-viewer';
6
7
  import {WebLogo} from '@datagrok-libraries/bio/src/viewers/web-logo';
7
8
 
8
9
  const vrt = VdRegionType;
@@ -36,7 +37,7 @@ const vrt = VdRegionType;
36
37
  /** Viewer with tabs based on description of chain regions.
37
38
  * Used to define regions of an immunoglobulin LC.
38
39
  */
39
- export class VdRegionsViewer extends DG.JsViewer {
40
+ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
40
41
  // private regionsDf: DG.DataFrame;
41
42
  private regionsFg: DG.FilterGroup | null = null;
42
43
  // private regionsTV: DG.TableView;
@@ -4,6 +4,64 @@ import * as DG from 'datagrok-api/dg';
4
4
  import {getMolfilesFromSingleSeq, HELM_CORE_LIB_FILENAME} from '../utils/utils';
5
5
  import {getMacroMol} from '../utils/atomic-works';
6
6
 
7
+ /**
8
+ * @export
9
+ * @param {DG.Column} col macromolecule cell.
10
+ * @return {Promise<DG.Widget>} Widget.
11
+ */
12
+ export function getMacroMolColumnPropertyPanel(col: DG.Column): DG.Widget {
13
+ const NONE = 'None';
14
+ const scaffoldColName = 'short';
15
+
16
+ // TODO: replace with an efficient version, bySemTypesExact won't help; GROK-8094
17
+ const columnsList = Array.from(col.dataFrame.columns as any).filter(
18
+ (c: any) => c.semType === DG.SEMTYPE.MOLECULE).map((c: any) => c.name);
19
+ const columnsSet = new Set(columnsList);
20
+ columnsSet.delete(col.name);
21
+
22
+ const monomerWidth = ui.choiceInput('Monomer width',
23
+ (col?.temp['monomer-width'] != null) ? col.temp['monomer-width'] : 'short',
24
+ ['short', 'long'],
25
+ (s: string) => {
26
+ col.temp['monomer-width'] = s;
27
+ col.setTag('.calculatedCellRender', '0');
28
+ col.dataFrame.fireValuesChanged();
29
+ });
30
+ monomerWidth.setTooltip('In short mode, only the first character should be visible, followed by .. if there are more characters');
31
+
32
+ const colorCode = ui.boolInput('Color code',
33
+ (col?.temp['color-code'] != null) ? col.temp['color-code'] : true,
34
+ (v: boolean) => {
35
+ col.temp['color-code'] = v;
36
+ col.dataFrame.fireValuesChanged();
37
+ });
38
+ colorCode.setTooltip('Color code');
39
+
40
+ const referenceSequence = ui.stringInput('Reference sequence', (col?.temp['reference-sequence'] != null) ? col?.temp['reference-sequence'] : '', (v: string) => {
41
+ col.temp['reference-sequence'] = v;
42
+ col.dataFrame.fireValuesChanged();
43
+ });
44
+ referenceSequence.setTooltip('Reference sequence is not empty, then the sequence will be render ' + '\n' +
45
+ 'as a difference from the reference sequence');
46
+
47
+ const compareWithCurrent = ui.boolInput('Compare with current',
48
+ (col?.temp['compare-with-current'] != null) ? col.temp['compare-with-current'] : true,
49
+ (v: boolean) => {
50
+ col.temp['compare-with-current'] = v;
51
+ col.dataFrame.fireValuesChanged();
52
+ });
53
+ compareWithCurrent.setTooltip('When on, all sequences get rendered in the "diff" mode');
54
+
55
+ const rdKitInputs = ui.inputs([
56
+ monomerWidth,
57
+ colorCode,
58
+ referenceSequence,
59
+ compareWithCurrent
60
+ ]);
61
+
62
+ return new DG.Widget(rdKitInputs);
63
+ }
64
+
7
65
  /**
8
66
  * 3D representation widget of macromolecule.
9
67
  *