@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.
- package/.eslintrc.json +17 -6
- package/CHANGELOG.md +4 -0
- package/dist/214.js +2 -0
- package/dist/436.js +2 -2
- package/dist/802.js +2 -0
- package/dist/package-test.js +2 -3
- package/dist/package.js +2 -3
- package/package.json +14 -14
- package/src/demo/fasta.ts +8 -2
- package/src/model.ts +783 -532
- package/src/package-test.ts +1 -3
- package/src/package.ts +15 -28
- package/src/tests/benchmarks.ts +31 -11
- package/src/tests/core.ts +11 -6
- package/src/tests/misc.ts +6 -6
- package/src/tests/model.ts +79 -44
- package/src/tests/table-view.ts +48 -38
- package/src/tests/utils.ts +0 -76
- package/src/tests/viewers.ts +30 -12
- package/src/tests/widgets.ts +30 -11
- package/src/utils/algorithms.ts +115 -38
- package/src/utils/cell-renderer.ts +181 -72
- package/src/utils/constants.ts +33 -7
- package/src/utils/misc.ts +244 -10
- package/src/utils/parallel-mutation-cliffs.ts +18 -15
- package/src/utils/statistics.ts +70 -15
- package/src/utils/tooltips.ts +42 -17
- package/src/utils/types.ts +29 -26
- package/src/utils/worker-creator.ts +5 -0
- package/src/viewers/logo-summary.ts +591 -130
- package/src/viewers/sar-viewer.ts +893 -239
- package/src/widgets/distribution.ts +305 -64
- package/src/widgets/manual-alignment.ts +18 -11
- package/src/widgets/mutation-cliffs.ts +44 -18
- package/src/widgets/peptides.ts +86 -91
- package/src/widgets/selection.ts +56 -22
- package/src/widgets/settings.ts +94 -44
- package/src/workers/mutation-cliffs-worker.ts +3 -16
- package/dist/209.js +0 -2
- package/dist/361.js +0 -2
- package/dist/381.js +0 -2
- package/dist/770.js +0 -2
- package/dist/831.js +0 -2
- package/dist/868.js +0 -2
- package/dist/931.js +0 -3
- package/dist/931.js.LICENSE.txt +0 -51
- package/dist/932.js +0 -2
- package/dist/package-test.js.LICENSE.txt +0 -51
- package/dist/package.js.LICENSE.txt +0 -51
- package/src/tests/peptide-space-test.ts +0 -48
- package/src/tests/test-data.ts +0 -649
- package/src/utils/molecular-measure.ts +0 -174
- package/src/utils/peptide-similarity-space.ts +0 -216
- package/src/viewers/peptide-space-viewer.ts +0 -150
- 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
|
-
|
|
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(
|
|
23
|
+
canvasContext.strokeRect(bounds.x + 1, bounds.y + 1, bounds.width - 1, bounds.height - 1);
|
|
18
24
|
}
|
|
19
25
|
|
|
20
|
-
/**
|
|
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.
|
|
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,
|
|
29
|
-
|
|
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(
|
|
62
|
+
DG.Color.scaleColor(x, -centeredPValLimit, centeredPValLimit));
|
|
45
63
|
|
|
46
|
-
const halfWidth =
|
|
47
|
-
const maxMeanDifference = Math.max(Math.abs(monomerPositionStats.general.minMeanDifference),
|
|
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(
|
|
53
|
-
const midY = Math.ceil(
|
|
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 =
|
|
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
|
-
|
|
97
|
+
|
|
98
|
+
const monomerSelection = viewer.mutationCliffsSelection[currentPosition];
|
|
79
99
|
if (monomerSelection && monomerSelection.includes(currentMonomer))
|
|
80
|
-
renderCellSelection(canvasContext,
|
|
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
|
-
|
|
85
|
-
|
|
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(
|
|
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(),
|
|
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,
|
|
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,
|
|
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(),
|
|
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) ||
|
|
112
|
-
|
|
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: {
|
|
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 ??=
|
|
123
|
-
drawOptions.marginHorizontal ??=
|
|
124
|
-
drawOptions.selectionWidth ??=
|
|
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
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
|
177
|
-
|
|
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
|
-
|
|
181
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
217
|
-
const fullStats = calculateMonomerPositionStatistics(df,
|
|
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,
|
|
225
|
-
|
|
226
|
-
|
|
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,
|
|
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
|
|
346
|
+
const monomerPosition = findWebLogoMonomerPosition(cell, ev, (options.webLogoBounds()));
|
|
257
347
|
if (monomerPosition === null) {
|
|
258
|
-
if (!options.isSelectionTable)
|
|
259
|
-
|
|
348
|
+
if (!options.isSelectionTable && options.unhighlightCallback != null)
|
|
349
|
+
options.unhighlightCallback();
|
|
350
|
+
|
|
351
|
+
|
|
260
352
|
return;
|
|
261
353
|
}
|
|
262
354
|
tooltipOptions.monomerPosition = monomerPosition;
|
|
263
|
-
|
|
264
|
-
if (!options.isSelectionTable)
|
|
265
|
-
|
|
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
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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,
|
|
388
|
+
options.cachedWebLogoTooltip()!.tooltip = showTooltipAt(df, activityCol, [], tooltipOptions);
|
|
288
389
|
}
|
|
289
390
|
}
|
|
290
391
|
}
|
|
291
392
|
|
|
292
|
-
|
|
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;
|
package/src/utils/constants.ts
CHANGED
|
@@ -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
|
|
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
|
+
};
|