@datagrok/peptides 1.9.2 → 1.11.3

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/src/model.ts CHANGED
@@ -12,15 +12,17 @@ import {DistanceMatrix} from '@datagrok-libraries/ml/src/distance-matrix';
12
12
  import {StringMetricsNames} from '@datagrok-libraries/ml/src/typed-metrics';
13
13
  import {ITreeHelper} from '@datagrok-libraries/bio/src/trees/tree-helper';
14
14
  import {TAGS as treeTAGS} from '@datagrok-libraries/bio/src/trees';
15
+ import BitArray from '@datagrok-libraries/utils/src/bit-array';
15
16
 
16
17
  import wu from 'wu';
17
18
  import * as rxjs from 'rxjs';
18
19
  import * as uuid from 'uuid';
20
+ import $ from 'cash-dom';
19
21
 
20
22
  import * as C from './utils/constants';
21
23
  import * as type from './utils/types';
22
- import {calculateSelected, extractColInfo, scaleActivity, getStatsSummary} from './utils/misc';
23
- import {MONOMER_POSITION_PROPERTIES, MonomerPosition, MostPotentResiduesViewer} from './viewers/sar-viewer';
24
+ import {calculateSelected, extractColInfo, scaleActivity, getStatsSummary, prepareTableForHistogram, getTemplate} from './utils/misc';
25
+ import {MONOMER_POSITION_PROPERTIES, MonomerPosition, MostPotentResidues} from './viewers/sar-viewer';
24
26
  import * as CR from './utils/cell-renderer';
25
27
  import {mutationCliffsWidget} from './widgets/mutation-cliffs';
26
28
  import {getActivityDistribution, getDistributionLegend, getDistributionWidget, getStatsTableMap,
@@ -31,7 +33,8 @@ import {getSettingsDialog} from './widgets/settings';
31
33
  import {_package, getMonomerWorksInstance, getTreeHelperInstance} from './package';
32
34
  import {findMutations} from './utils/algorithms';
33
35
  import {createDistanceMatrixWorker} from './utils/worker-creator';
34
- import BitArray from '@datagrok-libraries/utils/src/bit-array';
36
+ import {calculateIdentity, calculateSimilarity} from './widgets/similarity';
37
+ import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
35
38
 
36
39
  export type SummaryStats = {
37
40
  minCount: number, maxCount: number,
@@ -60,12 +63,6 @@ export const getAggregatedColName = (aggF: string, colName: string): string => `
60
63
  export class PeptidesModel {
61
64
  static modelName = 'peptidesModel';
62
65
 
63
- _settingsSubject: rxjs.Subject<type.PeptidesSettings> = new rxjs.Subject();
64
- _monomerPositionSelectionSubject: rxjs.Subject<undefined> = new rxjs.Subject();
65
- _newClusterSubject: rxjs.Subject<undefined> = new rxjs.Subject();
66
- _removeClusterSubject: rxjs.Subject<undefined> = new rxjs.Subject();
67
- _filterChangedSubject: rxjs.Subject<undefined> = new rxjs.Subject();
68
-
69
66
  _isUpdating: boolean = false;
70
67
  isBitsetChangedInitialized = false;
71
68
  isCellChanging = false;
@@ -75,8 +72,8 @@ export class PeptidesModel {
75
72
  splitCol!: DG.Column<boolean>;
76
73
  _monomerPositionStats?: MonomerPositionStats;
77
74
  _clusterStats?: ClusterTypeStats;
78
- _monomerPositionSelection!: type.PositionToAARList;
79
- _monomerPositionFilter!: type.PositionToAARList;
75
+ _mutationCliffsSelection!: type.PositionToAARList;
76
+ _invariantMapSelection!: type.PositionToAARList;
80
77
  _clusterSelection!: string[];
81
78
  _mutationCliffs: type.MutationCliffs | null = null;
82
79
  isInitialized = false;
@@ -90,8 +87,6 @@ export class PeptidesModel {
90
87
  isRibbonSet = false;
91
88
 
92
89
  _cp?: SeqPalette;
93
- initBitset: DG.BitSet;
94
- isInvariantMapTrigger: boolean = false;
95
90
  headerSelectedMonomers: type.MonomerSelectionStats = {};
96
91
  webLogoBounds: {[positon: string]: {[monomer: string]: DG.Rect}} = {};
97
92
  cachedWebLogoTooltip: {bar: string, tooltip: HTMLDivElement | null} = {bar: '', tooltip: null};
@@ -101,12 +96,12 @@ export class PeptidesModel {
101
96
  _matrixDf?: DG.DataFrame;
102
97
  _splitSeqDf?: DG.DataFrame;
103
98
  _distanceMatrix!: DistanceMatrix;
104
- _treeHelper!: ITreeHelper;
105
99
  _dm!: DistanceMatrix;
100
+ _layoutEventInitialized = false;
101
+ isToolboxSet: boolean = false;
106
102
 
107
103
  private constructor(dataFrame: DG.DataFrame) {
108
104
  this.df = dataFrame;
109
- this.initBitset = this.df.filter.clone();
110
105
  }
111
106
 
112
107
  static getInstance(dataFrame: DG.DataFrame): PeptidesModel {
@@ -123,11 +118,6 @@ export class PeptidesModel {
123
118
  return id;
124
119
  }
125
120
 
126
- get treeHelper(): ITreeHelper {
127
- this._treeHelper ??= getTreeHelperInstance();
128
- return this._treeHelper;
129
- }
130
-
131
121
  get monomerPositionDf(): DG.DataFrame {
132
122
  this._monomerPositionDf ??= this.createMonomerPositionDf();
133
123
  return this._monomerPositionDf;
@@ -146,15 +136,6 @@ export class PeptidesModel {
146
136
  this._monomerPositionStats = mps;
147
137
  }
148
138
 
149
- get matrixDf(): DG.DataFrame {
150
- this._matrixDf ??= this.buildMatrixDf();
151
- return this._matrixDf;
152
- }
153
-
154
- set matrixDf(df: DG.DataFrame) {
155
- this._matrixDf = df;
156
- }
157
-
158
139
  get splitSeqDf(): DG.DataFrame {
159
140
  this._splitSeqDf ??= this.buildSplitSeqDf();
160
141
  return this._splitSeqDf;
@@ -179,10 +160,6 @@ export class PeptidesModel {
179
160
  }
180
161
 
181
162
  get mutationCliffs(): type.MutationCliffs | null {
182
- // if (this._mutationCliffs)
183
- // return this._mutationCliffs;
184
-
185
- // this.updateMutationCliffs(false);
186
163
  return this._mutationCliffs!;
187
164
  }
188
165
 
@@ -209,59 +186,55 @@ export class PeptidesModel {
209
186
  }
210
187
 
211
188
  get analysisView(): DG.TableView {
212
- this._analysisView ??=
213
- wu(grok.shell.tableViews).find(({dataFrame}) => dataFrame.getTag(C.TAGS.UUID) === this.id) ??
214
- grok.shell.addTableView(this.df);
215
- if (this.df.getTag(C.TAGS.MULTIPLE_VIEWS) !== '1')
189
+ if (this._analysisView === undefined) {
190
+ this._analysisView = wu(grok.shell.tableViews).find(({dataFrame}) => dataFrame?.getTag(C.TAGS.UUID) === this.id);
191
+ if (this._analysisView === undefined) {
192
+ this._analysisView = grok.shell.addTableView(this.df);
193
+ const posCols = this.splitSeqDf.columns.names();
194
+
195
+ for (let colIdx = 1; colIdx < this._analysisView.grid.columns.length; ++colIdx) {
196
+ const gridCol = this._analysisView.grid.columns.byIndex(colIdx)!;
197
+ gridCol.visible =
198
+ posCols.includes(gridCol.column!.name) || (gridCol.column!.name === C.COLUMNS_NAMES.ACTIVITY_SCALED);
199
+ }
200
+ }
201
+ }
202
+
203
+ if (this.df.getTag(C.TAGS.MULTIPLE_VIEWS) !== '1' && !this._layoutEventInitialized)
216
204
  grok.shell.v = this._analysisView;
217
205
 
206
+ this._analysisView.grid.invalidate();
218
207
  return this._analysisView;
219
208
  }
220
209
 
221
- get onMonomerPositionSelectionChanged(): rxjs.Observable<undefined> {
222
- return this._monomerPositionSelectionSubject.asObservable();
223
- }
224
-
225
- get onSettingsChanged(): rxjs.Observable<type.PeptidesSettings> {
226
- return this._settingsSubject.asObservable();
227
- }
228
-
229
- get onNewCluster(): rxjs.Observable<undefined> {
230
- return this._newClusterSubject.asObservable();
231
- }
232
-
233
- get onRemoveCluster(): rxjs.Observable<undefined> {
234
- return this._removeClusterSubject.asObservable();
235
- }
236
-
237
- get onFilterChanged(): rxjs.Observable<undefined> {
238
- return this._filterChangedSubject.asObservable();
210
+ get mutationCliffsSelection(): type.PositionToAARList {
211
+ this._mutationCliffsSelection ??= JSON.parse(this.df.tags[C.TAGS.SELECTION] || '{}');
212
+ return this._mutationCliffsSelection;
239
213
  }
240
214
 
241
- get monomerPositionSelection(): type.PositionToAARList {
242
- this._monomerPositionSelection ??= JSON.parse(this.df.tags[C.TAGS.SELECTION] || '{}');
243
- return this._monomerPositionSelection;
244
- }
245
-
246
- set monomerPositionSelection(selection: type.PositionToAARList) {
247
- this._monomerPositionSelection = selection;
215
+ set mutationCliffsSelection(selection: type.PositionToAARList) {
216
+ this._mutationCliffsSelection = selection;
248
217
  this.df.tags[C.TAGS.SELECTION] = JSON.stringify(selection);
249
218
  this.fireBitsetChanged();
250
- this._monomerPositionSelectionSubject.next();
219
+
220
+ const mpViewer = this.findViewer(VIEWER_TYPE.MONOMER_POSITION) as MonomerPosition | null;
221
+ mpViewer?.viewerGrid.invalidate();
222
+ const mprViewer = this.findViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResidues | null;
223
+ mprViewer?.viewerGrid.invalidate();
224
+
251
225
  this.analysisView.grid.invalidate();
252
226
  }
253
227
 
254
- get monomerPositionFilter(): type.PositionToAARList {
255
- this._monomerPositionFilter ??= JSON.parse(this.df.tags[C.TAGS.FILTER] || '{}');
256
- return this._monomerPositionFilter;
228
+ get invariantMapSelection(): type.PositionToAARList {
229
+ this._invariantMapSelection ??=
230
+ JSON.parse(this.df.tags[C.TAGS.INVARIANT_MAP_SELECTION] || this.df.tags[C.TAGS.FILTER] || '{}');
231
+ return this._invariantMapSelection;
257
232
  }
258
233
 
259
- set monomerPositionFilter(selection: type.PositionToAARList) {
260
- this._monomerPositionFilter = selection;
261
- this.df.tags[C.TAGS.FILTER] = JSON.stringify(selection);
262
- this.isInvariantMapTrigger = true;
263
- this.fireBitsetChanged(true);
264
- this.isInvariantMapTrigger = false;
234
+ set invariantMapSelection(selection: type.PositionToAARList) {
235
+ this._invariantMapSelection = selection;
236
+ this.df.tags[C.TAGS.INVARIANT_MAP_SELECTION] = JSON.stringify(selection);
237
+ this.fireBitsetChanged();
265
238
  this.analysisView.grid.invalidate();
266
239
  }
267
240
 
@@ -298,7 +271,7 @@ export class PeptidesModel {
298
271
  }
299
272
 
300
273
  get isMonomerPositionSelectionEmpty(): boolean {
301
- for (const aarList of Object.values(this.monomerPositionSelection)) {
274
+ for (const aarList of Object.values(this.mutationCliffsSelection)) {
302
275
  if (aarList.length !== 0)
303
276
  return false;
304
277
  }
@@ -326,6 +299,7 @@ export class PeptidesModel {
326
299
  for (const [key, value] of newSettingsEntries) {
327
300
  this._settings[key as keyof type.PeptidesSettings] = value as any;
328
301
  switch (key) {
302
+ case 'activityColumnName':
329
303
  case 'scaling':
330
304
  updateVars.add('activity');
331
305
  updateVars.add('mutationCliffs');
@@ -363,12 +337,11 @@ export class PeptidesModel {
363
337
  break;
364
338
  case 'stats':
365
339
  this.monomerPositionStats = this.calculateMonomerPositionStatistics();
366
- this.monomerPositionDf = this.createMonomerPositionDf();
367
340
  this.mostPotentResiduesDf = this.createMostPotentResiduesDf();
368
341
  this.clusterStats = this.calculateClusterStatistics();
369
342
  break;
370
343
  case 'grid':
371
- this.postProcessGrids();
344
+ this.setGridProperties();
372
345
  break;
373
346
  case 'dendrogram':
374
347
  this.settings.showDendrogram ? this.addDendrogram() : this.closeViewer(VIEWER_TYPE.DENDROGRAM);
@@ -389,7 +362,23 @@ export class PeptidesModel {
389
362
  }
390
363
 
391
364
  //TODO: handle settings change
392
- this._settingsSubject.next(this.settings);
365
+ const mpViewer = this.findViewer(VIEWER_TYPE.MONOMER_POSITION) as MonomerPosition | null;
366
+ mpViewer?.createMonomerPositionGrid();
367
+ mpViewer?.render();
368
+ const mprViewer = this.findViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResidues | null;
369
+ mprViewer?.createMostPotentResiduesGrid();
370
+ mprViewer?.render();
371
+ const lstViewer = this.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable | null;
372
+ lstViewer?.createLogoSummaryTableGrid();
373
+ lstViewer?.render();
374
+ }
375
+
376
+ get identityTemplate(): string {
377
+ return this.df.getTag(C.TAGS.IDENTITY_TEMPLATE) ?? '';
378
+ }
379
+
380
+ set identityTemplate(template: string) {
381
+ this.df.setTag(C.TAGS.IDENTITY_TEMPLATE, template);
393
382
  }
394
383
 
395
384
  updateMutationCliffs(notify: boolean = true): void {
@@ -409,34 +398,23 @@ export class PeptidesModel {
409
398
  }
410
399
 
411
400
  createMonomerPositionDf(): DG.DataFrame {
412
- const positions = this.splitSeqDf.columns.names();
413
- const matrixDf = this.matrixDf
414
- .groupBy([C.COLUMNS_NAMES.MONOMER])
415
- .aggregate();
416
- for (const pos of positions)
417
- matrixDf.columns.addNewString(pos);
418
-
419
- const monomerCol = matrixDf.getCol(C.COLUMNS_NAMES.MONOMER);
420
- for (let i = 0; i < monomerCol.length; ++i) {
421
- if (monomerCol.get(i) === '') {
422
- matrixDf.rows.removeAt(i);
423
- break;
401
+ const uniqueMonomers = new Set<string>();
402
+ const splitSeqCols = this.splitSeqDf.columns;
403
+ for (const col of splitSeqCols) {
404
+ const colCat = col.categories;
405
+ for (const cat of colCat) {
406
+ if (cat !== '')
407
+ uniqueMonomers.add(cat);
424
408
  }
425
409
  }
426
- matrixDf.name = 'SAR';
427
410
 
428
- return matrixDf;
429
- }
411
+ const monomerCol = DG.Column.fromStrings(C.COLUMNS_NAMES.MONOMER, Array.from(uniqueMonomers));
412
+ const monomerPositionDf = DG.DataFrame.fromColumns([monomerCol]);
413
+ monomerPositionDf.name = 'SAR';
414
+ for (const col of splitSeqCols)
415
+ monomerPositionDf.columns.addNewBool(col.name);
430
416
 
431
- buildMatrixDf(): DG.DataFrame {
432
- const splitSeqDfColumns = this.splitSeqDf.columns;
433
- const positionColumns = splitSeqDfColumns.names();
434
- return this.splitSeqDf
435
- .groupBy(positionColumns)
436
- .aggregate()
437
- .unpivot([], positionColumns, C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER)
438
- .groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER])
439
- .aggregate();
417
+ return monomerPositionDf;
440
418
  }
441
419
 
442
420
  buildSplitSeqDf(): DG.DataFrame {
@@ -462,19 +440,35 @@ export class PeptidesModel {
462
440
  acc.addTitle(ui.h1(`${filterAndSelectionBs.trueCount} selected rows${filteredTitlePart}`));
463
441
  if (filterAndSelectionBs.anyTrue) {
464
442
  acc.addPane('Actions', () => {
465
- const newViewButton = ui.button('New view', () => trueModel.createNewView(),
466
- 'Creates a new view from current selection');
467
- const newCluster = ui.button('New cluster', () => trueModel._newClusterSubject.next(),
468
- 'Creates a new cluster from selection');
469
- const removeCluster = ui.button('Remove cluster', () => trueModel._removeClusterSubject.next(),
470
- 'Removes currently selected custom cluster');
471
- removeCluster.disabled = trueModel.clusterSelection.length === 0 ||
472
- !wu(this.customClusters).some((c) => trueModel.clusterSelection.includes(c.name));
473
- return ui.divV([newViewButton, newCluster, removeCluster]);
443
+ const newView = ui.label('New view');
444
+ $(newView).addClass('d4-link-action');
445
+ newView.onclick = () => trueModel.createNewView();
446
+ newView.onmouseover = (ev) => ui.tooltip.show('Creates a new view from current selection', ev.clientX + 5, ev.clientY + 5);
447
+ const newCluster = ui.label('New cluster');
448
+ $(newCluster).addClass('d4-link-action');
449
+ newCluster.onclick = () => {
450
+ const lstViewer = trueModel.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable | null;
451
+ if (lstViewer === null)
452
+ throw new Error('Logo summary table viewer is not found');
453
+ lstViewer.clusterFromSelection();
454
+ };
455
+ newCluster.onmouseover = (ev) => ui.tooltip.show('Creates a new cluster from selection', ev.clientX + 5, ev.clientY + 5);
456
+ const removeCluster = ui.label('Remove cluster');
457
+ $(removeCluster).addClass('d4-link-action');
458
+ removeCluster.onclick = () => {
459
+ const lstViewer = trueModel.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable | null;
460
+ if (lstViewer === null)
461
+ throw new Error('Logo summary table viewer is not found');
462
+ lstViewer.removeCluster();
463
+ };
464
+ removeCluster.onmouseover = (ev) => ui.tooltip.show('Removes currently selected custom cluster', ev.clientX + 5, ev.clientY + 5);
465
+ removeCluster.style.visibility = trueModel.clusterSelection.length === 0 ||
466
+ !wu(this.customClusters).some((c) => trueModel.clusterSelection.includes(c.name)) ? 'hidden' : 'visible';
467
+ return ui.divV([newView, newCluster, removeCluster]);
474
468
  });
475
469
  }
476
470
  const table = trueModel.df.filter.anyFalse ? trueModel.df.clone(trueModel.df.filter, null, true) : trueModel.df;
477
- acc.addPane('Mutation Cliff pairs', () => mutationCliffsWidget(trueModel.df, trueModel).root);
471
+ acc.addPane('Mutation Cliffs pairs', () => mutationCliffsWidget(trueModel.df, trueModel).root);
478
472
  acc.addPane('Distribution', () => getDistributionWidget(table, trueModel).root);
479
473
 
480
474
  return acc;
@@ -483,12 +477,10 @@ export class PeptidesModel {
483
477
  updateGrid(): void {
484
478
  this.joinDataFrames();
485
479
 
486
- this.sortSourceGrid();
487
-
488
480
  this.createScaledCol();
489
481
 
490
- this.initMonomerPositionFilter({notify: false});
491
- this.initMonomerPositionSelection({notify: false});
482
+ this.initInvariantMapSelection({notify: false});
483
+ this.initMutationCliffsSelection({notify: false});
492
484
 
493
485
  this.setWebLogoInteraction();
494
486
  this.webLogoBounds = {};
@@ -499,14 +491,14 @@ export class PeptidesModel {
499
491
 
500
492
  this.setBitsetCallback();
501
493
 
502
- this.postProcessGrids();
494
+ this.setGridProperties();
503
495
  }
504
496
 
505
- initMonomerPositionFilter(options: {cleanInit?: boolean, notify?: boolean} = {}): void {
497
+ initInvariantMapSelection(options: {cleanInit?: boolean, notify?: boolean} = {}): void {
506
498
  options.cleanInit ??= false;
507
499
  options.notify ??= true;
508
500
 
509
- const tempFilter: type.PositionToAARList = this.monomerPositionFilter;
501
+ const tempFilter: type.PositionToAARList = this.invariantMapSelection;
510
502
  const positionColumns = this.splitSeqDf.columns.names();
511
503
  for (const pos of positionColumns) {
512
504
  if (options.cleanInit || !tempFilter.hasOwnProperty(pos))
@@ -514,16 +506,16 @@ export class PeptidesModel {
514
506
  }
515
507
 
516
508
  if (options.notify)
517
- this.monomerPositionFilter = tempFilter;
509
+ this.invariantMapSelection = tempFilter;
518
510
  else
519
- this._monomerPositionFilter = tempFilter;
511
+ this._invariantMapSelection = tempFilter;
520
512
  }
521
513
 
522
- initMonomerPositionSelection(options: {cleanInit?: boolean, notify?: boolean} = {}): void {
514
+ initMutationCliffsSelection(options: {cleanInit?: boolean, notify?: boolean} = {}): void {
523
515
  options.cleanInit ??= false;
524
516
  options.notify ??= true;
525
517
 
526
- const tempSelection: type.PositionToAARList = this.monomerPositionSelection;
518
+ const tempSelection: type.PositionToAARList = this.mutationCliffsSelection;
527
519
  const positionColumns = this.splitSeqDf.columns.names();
528
520
  for (const pos of positionColumns) {
529
521
  if (options.cleanInit || !tempSelection.hasOwnProperty(pos))
@@ -531,9 +523,9 @@ export class PeptidesModel {
531
523
  }
532
524
 
533
525
  if (options.notify)
534
- this.monomerPositionSelection = tempSelection;
526
+ this.mutationCliffsSelection = tempSelection;
535
527
  else
536
- this._monomerPositionSelection = tempSelection;
528
+ this._mutationCliffsSelection = tempSelection;
537
529
  }
538
530
 
539
531
  joinDataFrames(): void {
@@ -542,39 +534,19 @@ export class PeptidesModel {
542
534
  const cols = this.df.columns;
543
535
  const positionColumns = this.splitSeqDf.columns.names();
544
536
  for (const colName of positionColumns) {
545
- const col = this.df.col(colName);
537
+ let col = this.df.col(colName);
546
538
  const newCol = this.splitSeqDf.getCol(colName);
547
- if (col === null)
548
- cols.add(newCol);
549
- else {
539
+ if (col !== null)
550
540
  cols.remove(colName);
551
- cols.add(newCol);
552
- }
553
- CR.setAARRenderer(newCol, this.alphabet);
541
+
542
+ const newColCat = newCol.categories;
543
+ const newColData = newCol.getRawData();
544
+ col = cols.addNew(newCol.name, newCol.type).init((i) => newColCat[newColData[i]]);
545
+ CR.setAARRenderer(col, this.alphabet);
554
546
  }
555
547
  this.df.name = name;
556
548
  }
557
549
 
558
- sortSourceGrid(): void {
559
- const colNames: DG.GridColumn[] = [];
560
- const sourceGridCols = this.analysisView.grid.columns;
561
- const sourceGridColsCount = sourceGridCols.length;
562
- for (let i = 1; i < sourceGridColsCount; i++)
563
- colNames.push(sourceGridCols.byIndex(i)!);
564
-
565
- colNames.sort((a, b) => {
566
- if (a.column!.semType === C.SEM_TYPES.MONOMER) {
567
- if (b.column!.semType === C.SEM_TYPES.MONOMER)
568
- return 0;
569
- return -1;
570
- }
571
- if (b.column!.semType === C.SEM_TYPES.MONOMER)
572
- return 1;
573
- return 0;
574
- });
575
- sourceGridCols.setOrder(colNames.map((v) => v.name));
576
- }
577
-
578
550
  createScaledCol(): void {
579
551
  const sourceGrid = this.analysisView.grid;
580
552
  const scaledCol = scaleActivity(this.df.getCol(this.settings.activityColumnName!), this.settings.scaling);
@@ -586,9 +558,9 @@ export class PeptidesModel {
586
558
 
587
559
  calculateMonomerPositionStatistics(): MonomerPositionStats {
588
560
  const positionColumns = this.splitSeqDf.columns.toList();
561
+ const sourceDfLen = this.df.rowCount;
589
562
  const monomerPositionObject = {general: {}} as MonomerPositionStats & { general: SummaryStats };
590
563
  const activityColData = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).getRawData();
591
- const sourceDfLen = activityColData.length;
592
564
 
593
565
  for (const posCol of positionColumns) {
594
566
  const posColData = posCol.getRawData();
@@ -600,7 +572,12 @@ export class PeptidesModel {
600
572
  if (monomer === '')
601
573
  continue;
602
574
 
603
- const bitArray = BitArray.fromSeq(sourceDfLen, (i: number) => posColData[i] === categoryIndex);
575
+ const boolArray: boolean[] = new Array(sourceDfLen).fill(false);
576
+ for (let i = 0; i < sourceDfLen; ++i) {
577
+ if (posColData[i] === categoryIndex)
578
+ boolArray[i] = true;
579
+ }
580
+ const bitArray = BitArray.fromValues(boolArray);
604
581
  const stats = getStats(activityColData, bitArray);
605
582
  currentPositionObject[monomer] = stats;
606
583
  this.getSummaryStats(currentPositionObject.general, stats);
@@ -658,12 +635,11 @@ export class PeptidesModel {
658
635
 
659
636
  calculateClusterStatistics(): ClusterTypeStats {
660
637
  const rowCount = this.df.rowCount;
661
-
662
638
  const origClustCol = this.df.getCol(this.settings.clustersColumnName!);
663
639
  const origClustColData = origClustCol.getRawData();
664
640
  const origClustColCat = origClustCol.categories;
665
641
  const origClustMasks: BitArray[] = Array.from({length: origClustColCat.length},
666
- () => BitArray.fromSeq(rowCount, (_: number) => false));
642
+ () => new BitArray(rowCount, false));
667
643
  for (let rowIdx = 0; rowIdx < rowCount; ++rowIdx)
668
644
  origClustMasks[origClustColData[rowIdx]].setTrue(rowIdx);
669
645
 
@@ -793,9 +769,9 @@ export class PeptidesModel {
793
769
  const position = barPart.position;
794
770
  if (ev.type === 'click') {
795
771
  if (!ev.shiftKey)
796
- this.initMonomerPositionSelection({cleanInit: true, notify: false});
772
+ this.initInvariantMapSelection({cleanInit: true, notify: false});
797
773
 
798
- this.modifyMonomerPositionSelection(monomer, position, false);
774
+ this.modifyMonomerPositionSelection(monomer, position, true);
799
775
  } else {
800
776
  const bar = `${position} = ${monomer}`;
801
777
  if (this.cachedWebLogoTooltip.bar === bar)
@@ -811,7 +787,7 @@ export class PeptidesModel {
811
787
  setCellRenderers(): void {
812
788
  const sourceGrid = this.analysisView.grid;
813
789
  sourceGrid.setOptions({'colHeaderHeight': 130});
814
- sourceGrid.onCellRender.subscribe((gcArgs) => {
790
+ const headerRenderer = (gcArgs: DG.GridCellRenderArgs): void => {
815
791
  const ctx = gcArgs.g;
816
792
  const bounds = gcArgs.bounds;
817
793
  const col = gcArgs.cell.tableColumn;
@@ -834,8 +810,8 @@ export class PeptidesModel {
834
810
  return 0;
835
811
  }).filter((v) => v !== 'general');
836
812
 
837
- this.webLogoBounds[col.name] = CR.drawLogoInBounds(ctx, bounds, stats, sortedStatsOrder, this.df.rowCount,
838
- this.cp, this.headerSelectedMonomers[col.name]);
813
+ this.webLogoBounds[col.name] = CR.drawLogoInBounds(ctx, bounds, stats, col.name, sortedStatsOrder,
814
+ this.df.rowCount, this.cp, this.headerSelectedMonomers[col.name]);
839
815
  gcArgs.preventDefault();
840
816
  }
841
817
  } catch (e) {
@@ -845,15 +821,28 @@ export class PeptidesModel {
845
821
  } finally {
846
822
  ctx.restore();
847
823
  }
848
- });
824
+ };
825
+ sourceGrid.onCellRender.subscribe((gcArgs) => headerRenderer(gcArgs));
826
+
827
+ if (!this._layoutEventInitialized) {
828
+ grok.events.onViewLayoutApplied.subscribe((layout) => {
829
+ if (layout.view.id === this.analysisView.id) {
830
+ // this.analysisView.grid.onCellRender.subscribe((gcArgs) => headerRenderer(gcArgs));
831
+ this.updateGrid();
832
+ }
833
+ });
834
+ this._layoutEventInitialized = true;
835
+ }
849
836
  }
850
837
 
851
838
  setTooltips(): void {
852
839
  this.analysisView.grid.onCellTooltip((cell, x, y) => {
853
- const col = cell.tableColumn;
854
- const cellValue = cell.cell.value;
855
- if (cellValue && col && col.semType === C.SEM_TYPES.MONOMER)
856
- this.showMonomerTooltip(cellValue, x, y);
840
+ if (cell.isColHeader && cell.tableColumn!.semType === C.SEM_TYPES.MONOMER)
841
+ return true;
842
+ if (!(cell.isTableCell && cell.tableColumn!.semType === C.SEM_TYPES.MONOMER))
843
+ return false;
844
+
845
+ this.showMonomerTooltip(cell.cell.value, x, y);
857
846
  return true;
858
847
  });
859
848
  }
@@ -863,18 +852,21 @@ export class PeptidesModel {
863
852
  const monomerName = aar.toLowerCase();
864
853
 
865
854
  const mw = getMonomerWorksInstance();
866
- const mol = mw.getCappedRotatedMonomer('PEPTIDE', aar);
855
+ const mol = mw?.getCappedRotatedMonomer('PEPTIDE', aar);
867
856
 
868
857
  if (mol) {
869
858
  tooltipElements.push(ui.div(monomerName));
870
859
  const options = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
871
860
  tooltipElements.push(grok.chem.svgMol(mol, undefined, undefined, options));
872
- } else
861
+ } else if (aar !== '')
873
862
  tooltipElements.push(ui.div(aar));
863
+ else
864
+ return true;
865
+
874
866
 
875
867
  ui.tooltip.show(ui.divV(tooltipElements), x, y);
876
868
 
877
- return mol !== null;
869
+ return true;
878
870
  }
879
871
 
880
872
  //TODO: move out to viewer code
@@ -893,12 +885,12 @@ export class PeptidesModel {
893
885
  const distributionTable = DG.DataFrame.fromColumns(
894
886
  [activityCol, DG.Column.fromBitSet(C.COLUMNS_NAMES.SPLIT_COL, mask)]);
895
887
  const labels = getDistributionLegend(`${position} : ${aar}`, 'Other');
896
- const hist = getActivityDistribution(distributionTable, true);
897
- const tableMap = getStatsTableMap(stats, {fractionDigits: 2});
898
- const aggregatedColMap = this.getAggregatedColumnValues({mask: mask, fractionDigits: 2});
888
+ const hist = getActivityDistribution(prepareTableForHistogram(distributionTable), true);
889
+ const tableMap = getStatsTableMap(stats);
890
+ const aggregatedColMap = this.getAggregatedColumnValues({mask: mask});
899
891
 
900
892
  const resultMap = {...tableMap, ...aggregatedColMap};
901
- const distroStatsElem = getStatsSummary(labels, hist, resultMap, true);
893
+ const distroStatsElem = getStatsSummary(labels, hist, resultMap);
902
894
 
903
895
  ui.tooltip.show(distroStatsElem, x, y);
904
896
 
@@ -908,6 +900,7 @@ export class PeptidesModel {
908
900
  getAggregatedColumnValues(options: {filterDf?: boolean, mask?: DG.BitSet, fractionDigits?: number} = {},
909
901
  ): StringDictionary {
910
902
  options.filterDf ??= false;
903
+ options.fractionDigits ??= 3;
911
904
 
912
905
  const filteredDf = options.filterDf && this.df.filter.anyFalse ? this.df.clone(this.df.filter) : this.df;
913
906
 
@@ -920,8 +913,11 @@ export class PeptidesModel {
920
913
  return colResults;
921
914
  }
922
915
 
923
- modifyMonomerPositionSelection(aar: string, position: string, isFilter: boolean): void {
924
- const tempSelection = isFilter ? this.monomerPositionFilter : this.monomerPositionSelection;
916
+ modifyMonomerPositionSelection(aar: string, position: string, isInvariantMap: boolean): void {
917
+ if (this.df.getCol(position).categories.indexOf(aar) === -1)
918
+ return;
919
+
920
+ const tempSelection = isInvariantMap ? this.invariantMapSelection : this.mutationCliffsSelection;
925
921
  const tempSelectionAt = tempSelection[position];
926
922
  const aarIndex = tempSelectionAt.indexOf(aar);
927
923
  if (aarIndex === -1)
@@ -929,10 +925,10 @@ export class PeptidesModel {
929
925
  else
930
926
  tempSelectionAt.splice(aarIndex, 1);
931
927
 
932
- if (isFilter)
933
- this.monomerPositionFilter = tempSelection;
928
+ if (isInvariantMap)
929
+ this.invariantMapSelection = tempSelection;
934
930
  else
935
- this.monomerPositionSelection = tempSelection;
931
+ this.mutationCliffsSelection = tempSelection;
936
932
  }
937
933
 
938
934
  setBitsetCallback(): void {
@@ -942,14 +938,45 @@ export class PeptidesModel {
942
938
  const filter = this.df.filter;
943
939
  const clusterCol = this.df.col(this.settings.clustersColumnName!);
944
940
 
945
- const changeSelectionBitset = (currentBitset: DG.BitSet, posList: type.RawColumn[], clustColCat: string[],
941
+ const changeSelectionBitset = (currentBitset: DG.BitSet, clustColCat: string[],
946
942
  clustColData: type.RawData, customClust: {[key: string]: BitArray}): void => {
947
- const getBitAt = (i: number): boolean => {
948
- for (const posRawCol of posList) {
949
- if (this.monomerPositionSelection[posRawCol.name].includes(posRawCol.cat![posRawCol.rawData[i]]))
950
- return true;
943
+ const indexes = new Set<number>();
944
+ for (const [position, monomerList] of Object.entries(this.mutationCliffsSelection)) {
945
+ for (const monomer of monomerList) {
946
+ const substitutions = this.mutationCliffs?.get(monomer)?.get(position) ?? null;
947
+ if (substitutions === null)
948
+ continue;
949
+ for (const [key, value] of substitutions.entries()) {
950
+ indexes.add(key);
951
+ for (const v of value)
952
+ indexes.add(v);
953
+ }
954
+ }
955
+ }
956
+
957
+ const positionList = Object.keys(this.invariantMapSelection);
958
+ const rowCount = this.df.rowCount;
959
+ for (const position of positionList) {
960
+ const positionCol: DG.Column<string> = this.df.getCol(position);
961
+ const positionColData = positionCol.getRawData();
962
+ const positionColCat = positionCol.categories;
963
+ const aarList = this.invariantMapSelection[position];
964
+ for (const aar of aarList) {
965
+ const aarIndex = positionColCat.indexOf(aar);
966
+ if (aarIndex === -1)
967
+ continue;
968
+ for (let i = 0; i < rowCount; ++i) {
969
+ if (positionColData[i] === aarIndex)
970
+ indexes.add(i);
971
+ }
951
972
  }
973
+ }
952
974
 
975
+ const getBitAt = (i: number): boolean => {
976
+ if (indexes.has(i))
977
+ return true;
978
+
979
+ //TODO: preprocess
953
980
  const currentOrigClust = clustColCat[clustColData[i]];
954
981
  if (typeof currentOrigClust === undefined)
955
982
  return false;
@@ -971,11 +998,6 @@ export class PeptidesModel {
971
998
  if (this.isUserChangedSelection)
972
999
  return;
973
1000
 
974
- const positionList: type.RawColumn[] = Object.keys(this.monomerPositionSelection).map((pos) => {
975
- const posCol = this.df.getCol(pos);
976
- return {name: pos, cat: posCol.categories, rawData: posCol.getRawData()};
977
- });
978
-
979
1001
  const clustColCat = clusterCol?.categories ?? [];
980
1002
  const clustColData = clusterCol?.getRawData() ?? new Int32Array(0);
981
1003
  const customClust: {[key: string]: BitArray} = {};
@@ -983,29 +1005,15 @@ export class PeptidesModel {
983
1005
  for (const clust of this.customClusters)
984
1006
  customClust[clust.name] = BitArray.fromUint32Array(rowCount, clust.getRawData() as Uint32Array);
985
1007
 
986
- changeSelectionBitset(selection, positionList, clustColCat, clustColData, customClust);
1008
+ changeSelectionBitset(selection, clustColCat, clustColData, customClust);
987
1009
  });
988
1010
 
989
1011
  filter.onChanged.subscribe(() => {
990
- const positionList = Object.keys(this.monomerPositionFilter);
991
- const invariantMapBitset = DG.BitSet.create(filter.length, (index) => {
992
- let result = true;
993
- for (const position of positionList) {
994
- const aarList = this.monomerPositionFilter[position];
995
- result &&= aarList.length === 0 || aarList.includes(this.df.get(position, index));
996
- if (!result)
997
- return result;
998
- }
999
- return result;
1000
- });
1001
-
1002
- if (!this.isInvariantMapTrigger)
1003
- this.initBitset = filter.clone();
1004
-
1005
- const temp = invariantMapBitset.and(this.initBitset);
1006
- filter.init((i) => temp.get(i), false);
1007
-
1008
- this._filterChangedSubject.next();
1012
+ const lstViewer = this.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable | null;
1013
+ if (lstViewer !== null && typeof lstViewer.model !== 'undefined') {
1014
+ lstViewer.createLogoSummaryTableGrid();
1015
+ lstViewer.render();
1016
+ }
1009
1017
  });
1010
1018
  this.isBitsetChangedInitialized = true;
1011
1019
  }
@@ -1027,23 +1035,13 @@ export class PeptidesModel {
1027
1035
  this.isUserChangedSelection = true;
1028
1036
  }
1029
1037
 
1030
- postProcessGrids(): void {
1031
- const posCols = this.splitSeqDf.columns.names();
1038
+ setGridProperties(props?: DG.IGridLookSettings): void {
1032
1039
  const sourceGrid = this.analysisView.grid;
1033
- const sourceGridCols = sourceGrid.columns;
1034
- const sourceGridColsLen = sourceGridCols.length;
1035
- const visibleColumns = Object.keys(this.settings.columns || {});
1036
1040
  const sourceGridProps = sourceGrid.props;
1037
- sourceGridProps.allowColSelection = false;
1038
- sourceGridProps.allowEdit = false;
1039
- sourceGridProps.showCurrentRowIndicator = false;
1041
+ sourceGridProps.allowColSelection = props?.allowColSelection ?? false;
1042
+ sourceGridProps.allowEdit = props?.allowEdit ?? false;
1043
+ sourceGridProps.showCurrentRowIndicator = props?.showCurrentRowIndicator ?? false;
1040
1044
  this.df.temp[C.EMBEDDING_STATUS] = false;
1041
- for (let colIdx = 1; colIdx < sourceGridColsLen; ++colIdx) {
1042
- const gridCol = sourceGridCols.byIndex(colIdx)!;
1043
- const tableColName = gridCol.column!.name;
1044
- gridCol.visible = posCols.includes(tableColName) || (tableColName === C.COLUMNS_NAMES.ACTIVITY_SCALED) ||
1045
- visibleColumns.includes(tableColName);
1046
- }
1047
1045
  }
1048
1046
 
1049
1047
  closeViewer(viewerType: VIEWER_TYPE): void {
@@ -1066,10 +1064,11 @@ export class PeptidesModel {
1066
1064
  const pepColValues: string[] = this.df.getCol(this.settings.sequenceColumnName!).toList();
1067
1065
  this._dm ??= new DistanceMatrix(await createDistanceMatrixWorker(pepColValues, StringMetricsNames.Levenshtein));
1068
1066
  const leafCol = this.df.col('~leaf-id') ?? this.df.columns.addNewString('~leaf-id').init((i) => i.toString());
1069
- const treeNode = await this.treeHelper.hierarchicalClusteringByDistance(this._dm, 'ward');
1067
+ const treeHelper: ITreeHelper = getTreeHelperInstance()!;
1068
+ const treeNode = await treeHelper.hierarchicalClusteringByDistance(this._dm, 'ward');
1070
1069
 
1071
- this.df.setTag(treeTAGS.NEWICK, this.treeHelper.toNewick(treeNode));
1072
- const leafOrdering = this.treeHelper.getLeafList(treeNode).map((leaf) => parseInt(leaf.name));
1070
+ this.df.setTag(treeTAGS.NEWICK, treeHelper.toNewick(treeNode));
1071
+ const leafOrdering = treeHelper.getLeafList(treeNode).map((leaf) => parseInt(leaf.name));
1073
1072
  this.analysisView.grid.setRowOrder(leafOrdering);
1074
1073
  const dendrogramViewer = await this.df.plot.fromType('Dendrogram', {nodeColumnName: leafCol.name}) as DG.JsViewer;
1075
1074
 
@@ -1105,18 +1104,88 @@ export class PeptidesModel {
1105
1104
  const settingsButton = ui.iconFA('wrench', () => getSettingsDialog(this), 'Peptides analysis settings');
1106
1105
  this.analysisView.setRibbonPanels([[settingsButton]], false);
1107
1106
  this.isRibbonSet = true;
1108
- grok.events.onResetFilterRequest.subscribe(() => {
1109
- this.isInvariantMapTrigger = true;
1110
- this.initMonomerPositionFilter({cleanInit: true});
1111
- this.isInvariantMapTrigger = false;
1112
- });
1107
+ this.updateGrid();
1108
+ }
1109
+
1110
+ if (!this.isToolboxSet && this.df.getTag(C.TAGS.MULTIPLE_VIEWS) !== '1') {
1111
+ let template: ISeqSplitted;
1112
+ const sequencesCol = this.df.getCol(this.settings.sequenceColumnName!);
1113
+ const minTemplateLength = this.splitSeqDf.columns.toList()
1114
+ .filter((col) => col.stats.missingValueCount === 0).length;
1115
+ const calculateIdentityBtn = ui.button('Identity', async () => {
1116
+ let identityScoresCol = calculateIdentity(template, this.splitSeqDf);
1117
+ identityScoresCol.name = this.df.columns.getUnusedName(identityScoresCol.name);
1118
+ identityScoresCol = this.df.columns.add(identityScoresCol);
1119
+ identityScoresCol.setTag(C.TAGS.IDENTITY_TEMPLATE, new Array(template).join(' '));
1120
+ }, 'Calculate identity');
1121
+ const calculateSimilarityBtn = ui.button('Similarity', async () => {
1122
+ let similarityScoresCol = await calculateSimilarity(template, this.splitSeqDf);
1123
+ similarityScoresCol.name = this.df.columns.getUnusedName(similarityScoresCol.name);
1124
+ similarityScoresCol = this.df.columns.add(similarityScoresCol);
1125
+ similarityScoresCol.setTag(C.TAGS.SIMILARITY_TEMPLATE, new Array(template).join(' '));
1126
+ }, 'Calculate similarity');
1127
+ const templateInput = ui.stringInput('Template', this.identityTemplate, async () => {
1128
+ this.identityTemplate = templateInput.value;
1129
+ if (isNaN(parseInt(templateInput.value))) {
1130
+ if (templateInput.value.length === 0) {
1131
+ calculateIdentityBtn.disabled = true;
1132
+ calculateSimilarityBtn.disabled = true;
1133
+ return;
1134
+ }
1135
+ try {
1136
+ template ??= await getTemplate(this.identityTemplate, sequencesCol);
1137
+ if (template.length < minTemplateLength) {
1138
+ grok.shell.warning(`Template length should be at least ${minTemplateLength} amino acids.`);
1139
+ calculateIdentityBtn.disabled = true;
1140
+ calculateSimilarityBtn.disabled = true;
1141
+ return;
1142
+ } else if (new Array(template).includes('') || new Array(template).includes('-')) {
1143
+ grok.shell.warning('Template shouldn\'t contain gaps or empty cells.');
1144
+ calculateIdentityBtn.disabled = true;
1145
+ calculateSimilarityBtn.disabled = true;
1146
+ return;
1147
+ }
1148
+ } catch (e) {
1149
+ grok.shell.warning(`Only ${sequencesCol.getTag(DG.TAGS.UNITS)} sequence format is supported.`);
1150
+ grok.log.warning(e as string);
1151
+ calculateIdentityBtn.disabled = true;
1152
+ return;
1153
+ }
1154
+ } else {
1155
+ const rowIndex = parseInt(templateInput.value) - 1;
1156
+ const selectedIndexes = this.df.filter.getSelectedIndexes();
1157
+ if (rowIndex < 0 || rowIndex >= selectedIndexes.length) {
1158
+ grok.shell.warning('Invalid row index');
1159
+ calculateIdentityBtn.disabled = true;
1160
+ calculateSimilarityBtn.disabled = true;
1161
+ return;
1162
+ }
1163
+ this.identityTemplate = sequencesCol.get(selectedIndexes[rowIndex]);
1164
+ }
1165
+ try {
1166
+ template = await getTemplate(this.identityTemplate, sequencesCol);
1167
+ } catch (e) {
1168
+ grok.shell.warning('Couldn\'t recognize sequence format.');
1169
+ grok.log.warning(e as string);
1170
+ calculateIdentityBtn.disabled = true;
1171
+ calculateSimilarityBtn.disabled = true;
1172
+ return;
1173
+ }
1174
+ calculateIdentityBtn.disabled = false;
1175
+ calculateSimilarityBtn.disabled = false;
1176
+ }, {placeholder: 'Sequence or row index...'});
1177
+ templateInput.setTooltip('Template sequence. Can be row index, peptide ID or sequence.');
1178
+ templateInput.fireChanged();
1179
+ const acc = this.analysisView.toolboxPage.accordion;
1180
+ acc.addPane('Sequence Identity and Similarity',
1181
+ () => ui.divV([ui.form([templateInput]), calculateIdentityBtn, calculateSimilarityBtn]), true, acc.panes[0]);
1182
+ this.isToolboxSet = true;
1113
1183
  }
1114
1184
 
1115
- this.updateGrid();
1116
1185
  this.fireBitsetChanged(true);
1117
- this.analysisView.grid.invalidate();
1118
1186
  if (typeof this.settings.targetColumnName === 'undefined')
1119
1187
  this.updateMutationCliffs();
1188
+ this.analysisView.grid.invalidate();
1120
1189
  }
1121
1190
 
1122
1191
  findViewer(viewerType: VIEWER_TYPE): DG.Viewer | null {
@@ -1132,11 +1201,12 @@ export class PeptidesModel {
1132
1201
  await this.addMonomerPosition();
1133
1202
  if (this.settings.showMostPotentResidues)
1134
1203
  await this.addMostPotentResidues();
1204
+ logoSummaryTable.viewerGrid.invalidate();
1135
1205
  }
1136
1206
 
1137
1207
  async addMonomerPosition(): Promise<void> {
1138
1208
  const monomerPosition = await this.df.plot.fromType(VIEWER_TYPE.MONOMER_POSITION) as MonomerPosition;
1139
- const mostPotentResidues = this.findViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResiduesViewer | null;
1209
+ const mostPotentResidues = this.findViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResidues | null;
1140
1210
  const dm = this.analysisView.dockManager;
1141
1211
  const [dockType, refNode, ratio] = mostPotentResidues === null ? [DG.DOCK_TYPE.DOWN, null, undefined] :
1142
1212
  [DG.DOCK_TYPE.LEFT, this.findViewerNode(VIEWER_TYPE.MOST_POTENT_RESIDUES), 0.7];
@@ -1151,7 +1221,7 @@ export class PeptidesModel {
1151
1221
 
1152
1222
  async addMostPotentResidues(): Promise<void> {
1153
1223
  const mostPotentResidues =
1154
- await this.df.plot.fromType(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResiduesViewer;
1224
+ await this.df.plot.fromType(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResidues;
1155
1225
  const monomerPosition = this.findViewer(VIEWER_TYPE.MONOMER_POSITION) as MonomerPosition | null;
1156
1226
  const dm = this.analysisView.dockManager;
1157
1227
  const [dockType, refNode, ratio] = monomerPosition === null ? [DG.DOCK_TYPE.DOWN, null, undefined] :