@datagrok/peptides 1.17.0 → 1.17.2

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.
Files changed (55) hide show
  1. package/.eslintrc.json +17 -6
  2. package/CHANGELOG.md +4 -0
  3. package/dist/214.js +2 -0
  4. package/dist/436.js +2 -2
  5. package/dist/802.js +2 -0
  6. package/dist/package-test.js +2 -3
  7. package/dist/package.js +2 -3
  8. package/package.json +14 -14
  9. package/src/demo/fasta.ts +8 -2
  10. package/src/model.ts +783 -532
  11. package/src/package-test.ts +1 -3
  12. package/src/package.ts +15 -28
  13. package/src/tests/benchmarks.ts +31 -11
  14. package/src/tests/core.ts +11 -6
  15. package/src/tests/misc.ts +6 -6
  16. package/src/tests/model.ts +79 -44
  17. package/src/tests/table-view.ts +48 -38
  18. package/src/tests/utils.ts +0 -76
  19. package/src/tests/viewers.ts +30 -12
  20. package/src/tests/widgets.ts +30 -11
  21. package/src/utils/algorithms.ts +115 -38
  22. package/src/utils/cell-renderer.ts +181 -72
  23. package/src/utils/constants.ts +33 -7
  24. package/src/utils/misc.ts +244 -10
  25. package/src/utils/parallel-mutation-cliffs.ts +18 -15
  26. package/src/utils/statistics.ts +70 -15
  27. package/src/utils/tooltips.ts +42 -17
  28. package/src/utils/types.ts +29 -26
  29. package/src/utils/worker-creator.ts +5 -0
  30. package/src/viewers/logo-summary.ts +591 -130
  31. package/src/viewers/sar-viewer.ts +893 -239
  32. package/src/widgets/distribution.ts +305 -64
  33. package/src/widgets/manual-alignment.ts +18 -11
  34. package/src/widgets/mutation-cliffs.ts +44 -18
  35. package/src/widgets/peptides.ts +86 -91
  36. package/src/widgets/selection.ts +56 -22
  37. package/src/widgets/settings.ts +94 -44
  38. package/src/workers/mutation-cliffs-worker.ts +3 -16
  39. package/dist/209.js +0 -2
  40. package/dist/361.js +0 -2
  41. package/dist/381.js +0 -2
  42. package/dist/770.js +0 -2
  43. package/dist/831.js +0 -2
  44. package/dist/868.js +0 -2
  45. package/dist/931.js +0 -3
  46. package/dist/931.js.LICENSE.txt +0 -51
  47. package/dist/932.js +0 -2
  48. package/dist/package-test.js.LICENSE.txt +0 -51
  49. package/dist/package.js.LICENSE.txt +0 -51
  50. package/src/tests/peptide-space-test.ts +0 -48
  51. package/src/tests/test-data.ts +0 -649
  52. package/src/utils/molecular-measure.ts +0 -174
  53. package/src/utils/peptide-similarity-space.ts +0 -216
  54. package/src/viewers/peptide-space-viewer.ts +0 -150
  55. package/src/workers/dimensionality-reducer.ts +0 -25
@@ -3,31 +3,48 @@ import * as DG from 'datagrok-api/dg';
3
3
 
4
4
  import * as C from './constants';
5
5
  import * as type from './types';
6
- import {CLUSTER_TYPE, PeptidesModel} from '../model';
7
6
  import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
8
7
  import {monomerToShort} from '@datagrok-libraries/bio/src/utils/macromolecule';
9
8
  import {calculateMonomerPositionStatistics} from './algorithms';
10
9
  import * as rxjs from 'rxjs';
11
10
  import {showTooltipAt, TooltipOptions} from './tooltips';
12
11
  import {MonomerPositionStats, MonomerPositionStatsCache, PositionStats} from './statistics';
13
-
14
- export function renderCellSelection(canvasContext: CanvasRenderingContext2D, bound: DG.Rect): void {
12
+ import {CLUSTER_TYPE} from '../viewers/logo-summary';
13
+ import {SARViewer} from '../viewers/sar-viewer';
14
+
15
+ /**
16
+ * Renders cell selection border.
17
+ * @param canvasContext - Canvas context.
18
+ * @param bounds - Cell bounds.
19
+ */
20
+ export function renderCellSelection(canvasContext: CanvasRenderingContext2D, bounds: DG.Rect): void {
15
21
  canvasContext.strokeStyle = DG.Color.toHtml(DG.Color.selectedRows);
16
22
  canvasContext.lineWidth = 3;
17
- canvasContext.strokeRect(bound.x + 1, bound.y + 1, bound.width - 1, bound.height - 1);
23
+ canvasContext.strokeRect(bounds.x + 1, bounds.y + 1, bounds.width - 1, bounds.height - 1);
18
24
  }
19
25
 
20
- /** A function that sets amino acid residue cell renderer to the specified column */
26
+ /**
27
+ * Sets amino acid residue cell renderer to the specified column.
28
+ * @param col - Column to set renderer to.
29
+ * @param alphabet - Sequence alphabet.
30
+ */
21
31
  export function setMonomerRenderer(col: DG.Column, alphabet: string): void {
22
32
  col.semType = C.SEM_TYPES.MONOMER;
23
33
  col.setTag(DG.TAGS.CELL_RENDERER, C.SEM_TYPES.MONOMER);
24
- col.tags[C.TAGS.ALPHABET] = alphabet;
34
+ col.setTag(C.TAGS.ALPHABET, alphabet);
25
35
  }
26
36
 
37
+ /**
38
+ * Renders mutation cliffs cell.
39
+ * @param canvasContext - Canvas context.
40
+ * @param currentMonomer - Current monomer.
41
+ * @param currentPosition - Current position.
42
+ * @param viewer - Viewer that requested rendering.
43
+ * @param bounds - Cell bounds.
44
+ */
27
45
  export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D, currentMonomer: string,
28
- currentPosition: string, monomerPositionStats: MonomerPositionStats, bound: DG.Rect,
29
- mutationCliffsSelection: type.Selection, substitutionsInfo: type.MutationCliffs | null = null): void {
30
- const positionStats = monomerPositionStats[currentPosition];
46
+ currentPosition: string, viewer: SARViewer, bounds: DG.Rect): void {
47
+ const positionStats = viewer.monomerPositionStats[currentPosition];
31
48
  const pVal = positionStats![currentMonomer]!.pValue;
32
49
  const currentMeanDifference = positionStats![currentMonomer]!.meanDifference;
33
50
 
@@ -40,17 +57,19 @@ export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D,
40
57
  const centeredPValLimit = Math.max(centeredMaxPValComplement, centeredMinPValComplement);
41
58
  const pValComplement = pVal === null ? 0 : 1 - pVal - pValCentering;
42
59
 
60
+ const x = currentMeanDifference >= 0 ? pValComplement : -pValComplement;
43
61
  const coef = DG.Color.toHtml(pVal === null ? DG.Color.lightLightGray :
44
- DG.Color.scaleColor(currentMeanDifference >= 0 ? pValComplement : -pValComplement, -centeredPValLimit, centeredPValLimit));
62
+ DG.Color.scaleColor(x, -centeredPValLimit, centeredPValLimit));
45
63
 
46
- const halfWidth = bound.width / 2;
47
- const maxMeanDifference = Math.max(Math.abs(monomerPositionStats.general.minMeanDifference), monomerPositionStats.general.maxMeanDifference);
64
+ const halfWidth = bounds.width / 2;
65
+ const maxMeanDifference = Math.max(Math.abs(viewer.monomerPositionStats.general.minMeanDifference),
66
+ viewer.monomerPositionStats.general.maxMeanDifference);
48
67
  const rCoef = Math.abs(currentMeanDifference) / maxMeanDifference;
49
68
  const maxRadius = 0.9 * halfWidth / 2; // Fill at most 90% of the half of the cell width
50
69
  const radius = Math.floor(maxRadius * rCoef);
51
70
 
52
- const midX = Math.ceil(bound.x + 1 + halfWidth);
53
- const midY = Math.ceil(bound.y + 1 + bound.height / 2);
71
+ const midX = Math.ceil(bounds.x + 1 + halfWidth);
72
+ const midY = Math.ceil(bounds.y + 1 + bounds.height / 2);
54
73
  canvasContext.beginPath();
55
74
  canvasContext.fillStyle = coef;
56
75
  canvasContext.arc(midX - halfWidth / 2, midY, radius < 3 || pVal === null ? 3 : radius, 0, Math.PI * 2, true);
@@ -64,7 +83,7 @@ export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D,
64
83
  canvasContext.shadowBlur = 5;
65
84
  canvasContext.shadowColor = DG.Color.toHtml(DG.Color.white);
66
85
  const uniqueValues = new Set<number>();
67
- const substitutions = substitutionsInfo?.get(currentMonomer)?.get(currentPosition)?.entries() ?? null;
86
+ const substitutions = viewer.mutationCliffs?.get(currentMonomer)?.get(currentPosition)?.entries() ?? null;
68
87
  if (substitutions !== null) {
69
88
  for (const [key, value] of substitutions) {
70
89
  uniqueValues.add(key);
@@ -75,64 +94,101 @@ export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D,
75
94
  if (uniqueValues.size !== 0)
76
95
  canvasContext.fillText(uniqueValues.size.toString(), midX + halfWidth - 5, midY, halfWidth - 5);
77
96
 
78
- const monomerSelection = mutationCliffsSelection[currentPosition];
97
+
98
+ const monomerSelection = viewer.mutationCliffsSelection[currentPosition];
79
99
  if (monomerSelection && monomerSelection.includes(currentMonomer))
80
- renderCellSelection(canvasContext, bound);
100
+ renderCellSelection(canvasContext, bounds);
81
101
  }
82
102
 
103
+ /**
104
+ * Renders invariant map cell.
105
+ * @param canvasContext - Canvas context.
106
+ * @param currentMonomer - Current monomer.
107
+ * @param currentPosition - Current position.
108
+ * @param invariantMapSelection - Invariant map selection.
109
+ * @param cellValue - Cell value.
110
+ * @param bounds - Cell bounds.
111
+ * @param color - Cell color.
112
+ */
83
113
  export function renderInvariantMapCell(canvasContext: CanvasRenderingContext2D, currentMonomer: string,
84
- currentPosition: string, invariantMapSelection: type.Selection, cellValue: number, bound: DG.Rect,
85
- color: number): void {
114
+ currentPosition: string, invariantMapSelection: type.Selection, cellValue: number, bounds: DG.Rect,
115
+ color: number): void {
86
116
  //FIXME: This is a hack, because `color` value sometimes comes incomplete. E.g. we found that here `color` value is
87
117
  // 255 and its contrast color would be black, which is not visible on blue (color code) background. The full number
88
118
  // is actually 4278190335.
89
119
  color = DG.Color.fromHtml(DG.Color.toHtml(color));
90
120
  canvasContext.fillStyle = DG.Color.toHtml(color);
91
- canvasContext.fillRect(bound.x, bound.y, bound.width, bound.height);
121
+ canvasContext.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
92
122
  canvasContext.font = '13px Roboto, Roboto Local, sans-serif';
93
123
  canvasContext.textAlign = 'center';
94
124
  canvasContext.textBaseline = 'middle';
95
125
  canvasContext.fillStyle = DG.Color.toHtml(DG.Color.getContrastColor(color));
96
- canvasContext.fillText(cellValue.toString(), bound.x + (bound.width / 2), bound.y + (bound.height / 2), bound.width);
126
+ canvasContext.fillText(cellValue.toString(), bounds.x + (bounds.width / 2), bounds.y + (bounds.height / 2),
127
+ bounds.width);
97
128
 
98
129
  const monomerSelection = invariantMapSelection[currentPosition];
99
130
  if (monomerSelection && monomerSelection.includes(currentMonomer))
100
- renderCellSelection(canvasContext, bound);
131
+ renderCellSelection(canvasContext, bounds);
101
132
  }
102
133
 
134
+ /**
135
+ * Renders logo summary table cell.
136
+ * @param canvasContext - Canvas context.
137
+ * @param cellValue - Cell value.
138
+ * @param clusterSelection - Cluster selection.
139
+ * @param bounds - Cell bounds.
140
+ */
103
141
  export function renderLogoSummaryCell(canvasContext: CanvasRenderingContext2D, cellValue: string,
104
- clusterSelection: type.Selection, bound: DG.Rect): void {
142
+ clusterSelection: type.Selection, bounds: DG.Rect): void {
105
143
  canvasContext.font = '13px Roboto, Roboto Local, sans-serif';
106
144
  canvasContext.textAlign = 'center';
107
145
  canvasContext.textBaseline = 'middle';
108
146
  canvasContext.fillStyle = '#000';
109
- canvasContext.fillText(cellValue.toString(), bound.x + (bound.width / 2), bound.y + (bound.height / 2), bound.width);
147
+ canvasContext.fillText(cellValue.toString(), bounds.x + (bounds.width / 2), bounds.y + (bounds.height / 2),
148
+ bounds.width);
110
149
 
111
- if (clusterSelection[CLUSTER_TYPE.CUSTOM].includes(cellValue) || clusterSelection[CLUSTER_TYPE.ORIGINAL].includes(cellValue))
112
- renderCellSelection(canvasContext, bound);
150
+ if (clusterSelection[CLUSTER_TYPE.CUSTOM].includes(cellValue) ||
151
+ clusterSelection[CLUSTER_TYPE.ORIGINAL].includes(cellValue))
152
+ renderCellSelection(canvasContext, bounds);
113
153
  }
114
154
 
155
+ /**
156
+ * Renders WebLogo in a cell.
157
+ * @param ctx - Canvas context.
158
+ * @param bounds - Cell bounds.
159
+ * @param stats - Position statistics.
160
+ * @param position - Position name.
161
+ * @param sortedOrder - Monomers order to render.
162
+ * @param rowCount - Total dataframe rows count.
163
+ * @param cp - Color palette.
164
+ * @param [monomerSelectionStats] - Monomer selection statistics.
165
+ * @param [drawOptions] - Drawing options.
166
+ * @return - WebLogo monomer bounds.
167
+ */
115
168
  export function drawLogoInBounds(ctx: CanvasRenderingContext2D, bounds: DG.Rect, stats: PositionStats, position: string,
116
- sortedOrder: string[], rowCount: number, cp: SeqPalette, monomerSelectionStats: { [monomer: string]: number } = {},
169
+ sortedOrder: string[], rowCount: number, cp: SeqPalette, monomerSelectionStats: {
170
+ [monomer: string]: number
171
+ } = {},
117
172
  drawOptions: type.DrawOptions = {}): { [monomer: string]: DG.Rect } {
118
173
  const pr = window.devicePixelRatio;
119
174
  drawOptions.symbolStyle ??= '16px Roboto, Roboto Local, sans-serif';
120
175
  drawOptions.upperLetterHeight ??= 12.2;
121
176
  drawOptions.upperLetterAscent ??= 0.25;
122
- drawOptions.marginVertical ??= 2;
123
- drawOptions.marginHorizontal ??= 2;
124
- drawOptions.selectionWidth ??= 1;
177
+ drawOptions.marginVertical ??= 1;
178
+ drawOptions.marginHorizontal ??= 1;
179
+ drawOptions.selectionWidth ??= 2;
125
180
  drawOptions.textHeight ??= 13;
126
181
  drawOptions.headerStyle ??= `bold ${drawOptions.textHeight * pr}px Roboto, Roboto Local, sans-serif`;
127
182
 
128
183
  const totalSpace = (sortedOrder.length - 1) * drawOptions.upperLetterAscent; // Total space between letters
184
+ let currentY = (bounds.y + drawOptions.marginVertical) * pr;
129
185
  const barHeight = (bounds.height - 2 * drawOptions.marginVertical - totalSpace - 1.25 * drawOptions.textHeight) * pr;
130
- const leftShift = drawOptions.marginHorizontal * 2;
186
+
187
+ const xSelection = (bounds.x + drawOptions.marginHorizontal) * pr;
188
+ const selectionWidth = Math.max(drawOptions.selectionWidth * pr, 0.05 * bounds.width * pr);
189
+ const leftShift = drawOptions.marginHorizontal * 2 + drawOptions.selectionWidth;
131
190
  const barWidth = (bounds.width - (leftShift + drawOptions.marginHorizontal)) * pr;
132
191
  const xStart = (bounds.x + leftShift) * pr;
133
- const selectionWidth = Math.min(drawOptions.selectionWidth * pr, 1);
134
- const xSelection = (bounds.x + 1) * pr;
135
- let currentY = (bounds.y + drawOptions.marginVertical) * pr;
136
192
 
137
193
  const monomerBounds: { [monomer: string]: DG.Rect } = {};
138
194
  for (const monomer of sortedOrder) {
@@ -145,9 +201,11 @@ export function drawLogoInBounds(ctx: CanvasRenderingContext2D, bounds: DG.Rect,
145
201
  const monomerTxt = monomerToShort(monomer, 5);
146
202
  const mTm: TextMetrics = ctx.measureText(monomerTxt);
147
203
 
148
- // Filling selection
149
- ctx.lineWidth = selectionWidth;
150
- ctx.line(xSelection, currentY, xSelection, currentY + selectionHeight, DG.Color.rowSelection);
204
+ if (selectionHeight > 0) {
205
+ // Filling selection
206
+ ctx.lineWidth = selectionWidth;
207
+ ctx.line(xSelection, currentY, xSelection, currentY + selectionHeight, DG.Color.rowSelection);
208
+ }
151
209
 
152
210
  ctx.fillStyle = cp.get(monomer) ?? cp.get('other');
153
211
  ctx.textAlign = 'left';
@@ -157,7 +215,7 @@ export function drawLogoInBounds(ctx: CanvasRenderingContext2D, bounds: DG.Rect,
157
215
  const widthTransform = barWidth / mTm.width;
158
216
  const heightTransform = monomerHeight / drawOptions.upperLetterHeight;
159
217
  ctx.setTransform(widthTransform, 0, 0, heightTransform, xStart, currentY);
160
- ctx.fillText(monomerTxt, 0, 0);
218
+ ctx.fillText(monomerTxt, 0, 0, mTm.width);
161
219
  }
162
220
  currentY += monomerHeight + drawOptions.upperLetterAscent * pr;
163
221
  }
@@ -173,22 +231,43 @@ export function drawLogoInBounds(ctx: CanvasRenderingContext2D, bounds: DG.Rect,
173
231
  return monomerBounds;
174
232
  }
175
233
 
176
- export type CellRendererOptions = {isSelectionTable?: boolean, headerSelectedMonomers?: type.SelectionStats,
177
- webLogoBounds?: WebLogoBounds, cachedWebLogoTooltip?: type.CachedWebLogoTooltip};
234
+ export type WebLogoCellRendererOptions = {
235
+ isSelectionTable?: boolean,
236
+ headerSelectedMonomers?: () => type.SelectionStats,
237
+ webLogoBounds: () => WebLogoBounds,
238
+ cachedWebLogoTooltip: () => type.CachedWebLogoTooltip,
239
+ colorPalette: () => SeqPalette,
240
+ unhighlightCallback?: () => void,
241
+ highlightCallback?: (mp: type.SelectionItem, dataFrame: DG.DataFrame, stats: MonomerPositionStats) => void,
242
+ selectionCallback?: (monomerPosition: type.SelectionItem, options: type.SelectionOptions) => void,
243
+ };
178
244
  export type WebLogoBounds = { [position: string]: { [monomer: string]: DG.Rect } };
179
245
 
180
- export function setWebLogoRenderer(grid: DG.Grid, model: PeptidesModel, options: CellRendererOptions = {},
181
- tooltipOptions: TooltipOptions = {x: 0, y: 0, mpStats: {} as MonomerPositionStats, monomerPosition: {} as type.SelectionItem}): void {
246
+ /**
247
+ * Sets WebLogo renderer.
248
+ * @param grid - Grid to set renderer to.
249
+ * @param monomerPositionStats - Monomer position statistics.
250
+ * @param positionColumns - Position columns.
251
+ * @param activityCol - Activity column.
252
+ * @param options - Cell renderer options.
253
+ * @param tooltipOptions - Tooltip options.
254
+ */
255
+ export function setWebLogoRenderer(grid: DG.Grid, monomerPositionStats: MonomerPositionStats,
256
+ positionColumns: DG.Column<string>[], activityCol: DG.Column<number>, options: WebLogoCellRendererOptions,
257
+ tooltipOptions: TooltipOptions = {
258
+ x: 0, y: 0, mpStats: {} as MonomerPositionStats,
259
+ monomerPosition: {} as type.SelectionItem,
260
+ }): void {
182
261
  options.isSelectionTable ??= false;
183
262
  if (Object.keys(tooltipOptions.mpStats).length == 0)
184
- tooltipOptions.mpStats = model.monomerPositionStats;
263
+ tooltipOptions.mpStats = monomerPositionStats;
264
+
265
+
185
266
  if (options.isSelectionTable && (!options.webLogoBounds || !options.cachedWebLogoTooltip)) {
186
267
  throw new Error('Peptides: Cannot set WebLogo renderer for selection table without `headerSelectedMonomers`, ' +
187
268
  '`webLogoBounds` and `cachedWebLogoTooltip` options.');
188
269
  }
189
270
 
190
- options.webLogoBounds ??= model.webLogoBounds;
191
- options.cachedWebLogoTooltip ??= model.cachedWebLogoTooltip;
192
271
  const df = grid.dataFrame;
193
272
  grid.setOptions({'colHeaderHeight': 130});
194
273
  const headerRenderer = (gcArgs: DG.GridCellRenderArgs): void => {
@@ -205,39 +284,50 @@ export function setWebLogoRenderer(grid: DG.Grid, model: PeptidesModel, options:
205
284
  //TODO: optimize
206
285
  if (gcArgs.cell.isColHeader && col?.semType === C.SEM_TYPES.MONOMER) {
207
286
  const isDfFiltered = df.filter.anyFalse;
208
- let stats: PositionStats | undefined = undefined;
287
+ let stats: PositionStats | undefined;
209
288
  if (isDfFiltered) {
210
289
  const cache: MonomerPositionStatsCache = df.temp[C.TAGS.M_P_STATS_CACHE] ?? {};
211
290
  const colCache = cache?.[col.name];
212
291
  const dfFilterBuffer = df.filter.getBuffer();
213
292
  if (cache && colCache && colCache.filter.length === dfFilterBuffer.length &&
214
- colCache.filter.every((v, i) => v === dfFilterBuffer[i])) {
293
+ colCache.filter.every((v, i) => v === dfFilterBuffer[i]))
215
294
  stats = colCache.stats[col.name];
216
- } else {
217
- const fullStats = calculateMonomerPositionStatistics(df, model.positionColumns.toArray(), {isFiltered: true, columns: [col.name]});
295
+ else {
296
+ const fullStats = calculateMonomerPositionStatistics(activityCol, df.filter, positionColumns, {
297
+ isFiltered: true,
298
+ columns: [col.name],
299
+ });
218
300
  stats = fullStats[col.name];
219
301
  cache[col.name] = {filter: df.filter.getBuffer(), stats: fullStats, selection: df.selection.getBuffer()};
220
302
  }
221
-
222
303
  df.temp[C.TAGS.M_P_STATS_CACHE] = cache;
223
- } else if (options.isSelectionTable){
224
- stats = calculateMonomerPositionStatistics(df, model.positionColumns.toArray(), {isFiltered: true, columns: [col.name]})[col.name];
225
- } else {
226
- stats = model.monomerPositionStats[col.name];
227
- }
304
+ } else if (options.isSelectionTable) {
305
+ stats = calculateMonomerPositionStatistics(activityCol, df.filter, positionColumns, {
306
+ isFiltered: true,
307
+ columns: [col.name],
308
+ })[col.name];
309
+ } else
310
+ stats = monomerPositionStats[col.name];
311
+
312
+
228
313
  if (!stats)
229
314
  return;
315
+
316
+
230
317
  //TODO: precalc on stats creation
231
318
  const sortedStatsOrder = Object.keys(stats).sort((a, b) => {
232
319
  if (a === '' || a === '-')
233
320
  return +1;
234
321
  else if (b === '' || b === '-')
235
322
  return -1;
323
+
324
+
236
325
  return 0;
237
326
  }).filter((v) => v !== 'general');
238
327
 
239
- options.webLogoBounds![col.name] = drawLogoInBounds(ctx, bounds, stats, col.name,
240
- sortedStatsOrder, df.filter.trueCount, model.cp, options.headerSelectedMonomers ? options.headerSelectedMonomers[col.name] : {});
328
+ options.webLogoBounds()![col.name] = drawLogoInBounds(ctx, bounds, stats, col.name,
329
+ sortedStatsOrder, df.filter.trueCount, options.colorPalette(),
330
+ options.headerSelectedMonomers ? options.headerSelectedMonomers()[col.name] : {});
241
331
  gcArgs.preventDefault();
242
332
  }
243
333
  } catch (e) {
@@ -253,16 +343,18 @@ export function setWebLogoRenderer(grid: DG.Grid, model: PeptidesModel, options:
253
343
  const eventAction = (ev: MouseEvent): void => {
254
344
  const cell = grid.hitTest(ev.offsetX, ev.offsetY);
255
345
  if (cell?.isColHeader && cell.tableColumn?.semType === C.SEM_TYPES.MONOMER) {
256
- const monomerPosition = findWebLogoMonomerPosition(cell, ev, (options.webLogoBounds ?? model.webLogoBounds));
346
+ const monomerPosition = findWebLogoMonomerPosition(cell, ev, (options.webLogoBounds()));
257
347
  if (monomerPosition === null) {
258
- if (!options.isSelectionTable)
259
- model.unhighlight();
348
+ if (!options.isSelectionTable && options.unhighlightCallback != null)
349
+ options.unhighlightCallback();
350
+
351
+
260
352
  return;
261
353
  }
262
354
  tooltipOptions.monomerPosition = monomerPosition;
263
- requestBarchartAction(ev, monomerPosition, model, df, options, tooltipOptions);
264
- if (!options.isSelectionTable)
265
- model.highlightMonomerPosition(monomerPosition);
355
+ requestWebLogoAction(ev, monomerPosition, df, activityCol, options, tooltipOptions);
356
+ if (!options.isSelectionTable && options.highlightCallback != null)
357
+ options.highlightCallback(monomerPosition, df, monomerPositionStats);
266
358
  }
267
359
  };
268
360
 
@@ -271,25 +363,42 @@ export function setWebLogoRenderer(grid: DG.Grid, model: PeptidesModel, options:
271
363
  rxjs.fromEvent<MouseEvent>(grid.overlay, 'click').subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
272
364
  }
273
365
 
274
- function requestBarchartAction(ev: MouseEvent, monomerPosition: type.SelectionItem, model: PeptidesModel, df: DG.DataFrame,
275
- options: CellRendererOptions, tooltipOptions: TooltipOptions): void {
276
- if (ev.type === 'click' && !options.isSelectionTable)
277
- model.modifyInvariantMapSelection(monomerPosition, {shiftPressed: ev.shiftKey, ctrlPressed: ev.ctrlKey});
366
+ /**
367
+ * Handles WebLogoAction action.
368
+ * @param ev - Mouse event.
369
+ * @param monomerPosition - Monomer-position object.
370
+ * @param df - Dataframe with WebLogo in header.
371
+ * @param activityCol - Activity column.
372
+ * @param options - WebLogo cell renderer options.
373
+ * @param tooltipOptions - Tooltip options.
374
+ */
375
+ function requestWebLogoAction(ev: MouseEvent, monomerPosition: type.SelectionItem, df: DG.DataFrame,
376
+ activityCol: DG.Column<number>, options: WebLogoCellRendererOptions, tooltipOptions: TooltipOptions): void {
377
+ if (ev.type === 'click' && !options.isSelectionTable && options.selectionCallback != null)
378
+ options.selectionCallback(monomerPosition, {shiftPressed: ev.shiftKey, ctrlPressed: ev.ctrlKey});
278
379
  else {
279
380
  const bar = `${monomerPosition.positionOrClusterType} = ${monomerPosition.monomerOrCluster}`;
280
- if (options.cachedWebLogoTooltip!.bar === bar)
281
- ui.tooltip.show(options.cachedWebLogoTooltip!.tooltip!, ev.clientX, ev.clientY);
381
+ if (options.cachedWebLogoTooltip()!.bar === bar)
382
+ ui.tooltip.show(options.cachedWebLogoTooltip()!.tooltip!, ev.clientX, ev.clientY);
282
383
  else {
283
- options.cachedWebLogoTooltip!.bar = bar;
384
+ options.cachedWebLogoTooltip()!.bar = bar;
284
385
  tooltipOptions.x = ev.clientX;
285
386
  tooltipOptions.y = ev.clientY;
286
387
  tooltipOptions.monomerPosition = monomerPosition;
287
- options.cachedWebLogoTooltip!.tooltip = showTooltipAt(df, model.settings.columns!, tooltipOptions);
388
+ options.cachedWebLogoTooltip()!.tooltip = showTooltipAt(df, activityCol, [], tooltipOptions);
288
389
  }
289
390
  }
290
391
  }
291
392
 
292
- function findWebLogoMonomerPosition(cell: DG.GridCell, ev: MouseEvent, webLogoBounds: WebLogoBounds): type.SelectionItem | null {
393
+ /**
394
+ * Finds monomer-position pair in a grid cell with WebLogo render.
395
+ * @param cell - Grid cell to look for monomer-position pair.
396
+ * @param ev - Mouse event.
397
+ * @param webLogoBounds - Monomer bounds in WebLogo position.
398
+ * @return - Monomer-position pair.
399
+ */
400
+ function findWebLogoMonomerPosition(cell: DG.GridCell, ev: MouseEvent, webLogoBounds: WebLogoBounds,
401
+ ): type.SelectionItem | null {
293
402
  const barCoords = webLogoBounds[cell.tableColumn!.name];
294
403
  for (const [monomer, coords] of Object.entries(barCoords)) {
295
404
  const isIntersectingX = ev.offsetX >= coords.x && ev.offsetX <= coords.x + coords.width;
@@ -1,3 +1,5 @@
1
+ import * as DG from 'datagrok-api/dg';
2
+
1
3
  export enum COLUMNS_NAMES {
2
4
  SPLIT_COL = '~split',
3
5
  ACTIVITY = 'Activity',
@@ -22,11 +24,6 @@ export enum LST_COLUMN_NAMES {
22
24
  CLUSTER = 'Cluster',
23
25
  }
24
26
 
25
- export enum CATEGORIES {
26
- OTHER = 'Other',
27
- ALL = 'All',
28
- }
29
-
30
27
  export enum TAGS {
31
28
  MONOMER = 'monomer',
32
29
  POSITION = 'pos',
@@ -41,7 +38,7 @@ export enum TAGS {
41
38
  VISIBLE = 'visible',
42
39
  SETTINGS = 'settings',
43
40
  CUSTOM_CLUSTER = 'customCluster',
44
- UUID = 'pep-uuid',
41
+ // UUID = 'pep-uuid',
45
42
  MONOMER_POSITION_MODE = 'monomerPositionMode',
46
43
  MULTIPLE_VIEWS = 'isMultipleViews',
47
44
  IDENTITY_TEMPLATE = 'Identity template',
@@ -57,10 +54,39 @@ export enum SEM_TYPES {
57
54
  MACROMOLECULE_DIFFERENCE = 'MacromoleculeDifference',
58
55
  }
59
56
 
60
- export const EMBEDDING_STATUS = 'embeddingStatus';
57
+ export const COLUMN_NAME = 'ColumnName';
61
58
 
62
59
  export enum SCALING_METHODS {
63
60
  NONE = 'none',
64
61
  LG = 'lg',
65
62
  MINUS_LG = '-lg',
66
63
  }
64
+
65
+ export enum SUFFIXES {
66
+ LST = 'lst-', // Logo Summary Table
67
+ MP = 'mp-', // Monomer Position
68
+ MPR = 'mpr-', // Most Potent Residues
69
+ WL = 'wl-', // Web Logo
70
+ }
71
+
72
+ export const AGGREGATION_TYPES = Object.values(DG.AGG)
73
+ .filter((it) => ![DG.AGG.FIRST, DG.AGG.KEY, DG.AGG.PIVOT, DG.AGG.SELECTED_ROWS_COUNT].includes(it));
74
+
75
+ export const AGG_STATS_MAPPING: {[key: string]: string} = {
76
+ [DG.AGG.TOTAL_COUNT]: 'totalCount',
77
+ [DG.AGG.VALUE_COUNT]: 'valueCount',
78
+ [DG.AGG.UNIQUE_COUNT]: 'uniqueCount',
79
+ [DG.AGG.MISSING_VALUE_COUNT]: 'missingValueCount',
80
+ [DG.AGG.MIN]: 'min',
81
+ [DG.AGG.MAX]: 'max',
82
+ [DG.AGG.SUM]: 'sum',
83
+ [DG.AGG.MED]: 'med',
84
+ [DG.AGG.AVG]: 'avg',
85
+ [DG.AGG.STDEV]: 'stdev',
86
+ [DG.AGG.VARIANCE]: 'variance',
87
+ [DG.AGG.SKEW]: 'skew',
88
+ [DG.AGG.KURT]: 'kurt',
89
+ [DG.AGG.Q1]: 'q1',
90
+ [DG.AGG.Q2]: 'q2',
91
+ [DG.AGG.Q3]: 'q3',
92
+ };