@datagrok/bio 2.15.7 → 2.15.8

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.
@@ -7,19 +7,21 @@ import {HelmType} from '@datagrok-libraries/bio/src/helm/types';
7
7
  */
8
8
  export const naturalMonomerColors = {
9
9
  [HelmTypes.BASE]: {
10
- A: "#A0A0FF",
11
- G: "#FF7070",
12
- T: "#A0FFA0",
13
- C: "#FF8C4B",
14
- U: "#FF8080"
10
+ // Chromatogram palette // HELMWebEditor monomerColors
11
+ A: "green", // "#A0A0FF",
12
+ G: "black", // "#FF7070",
13
+ T: "red", // "#A0FFA0",
14
+ C: "blue", // "#FF8C4B",
15
+ U: "red", // "#FF8080"
15
16
  },
16
17
 
17
18
  [HelmTypes.NUCLEOTIDE]: {
18
- A: "#A0A0FF",
19
- G: "#FF7070",
20
- T: "#A0FFA0",
21
- C: "#FF8C4B",
22
- U: "#FF8080"
19
+ // Chromatogram palette // HELMWebEditor monomerColors
20
+ A: "green", // "#A0A0FF",
21
+ G: "black", // "#FF7070",
22
+ T: "red", // "#A0FFA0",
23
+ C: "blue", // "#FF8C4B",
24
+ U: "red", // "#FF8080"
23
25
  },
24
26
 
25
27
  [HelmTypes.LINKER]: {
@@ -34,26 +36,27 @@ export const naturalMonomerColors = {
34
36
  },
35
37
 
36
38
  [HelmTypes.AA]: {
37
- A: "#C8C8C8",
38
- R: "#145AFF",
39
- N: "#00DCDC",
40
- D: "#E60A0A",
41
- C: "#E6E600",
42
- E: "#00DCDC",
43
- Q: "#E60A0A",
44
- G: "#EBEBEB",
45
- H: "#8282D2",
46
- I: "#0F820F",
47
- L: "#0F820F",
48
- K: "#145AFF",
49
- M: "#E6E600",
50
- F: "#3232AA",
51
- P: "#DC9682",
52
- S: "#FA9600",
53
- T: "#FA9600",
54
- W: "#B45AB4",
55
- Y: "#3232AA",
56
- V: "#0F820F"
39
+ // GrokGroups palette // HELMWebEditor monomerColors
40
+ A: "rgb(44,160,44)", // "#C8C8C8",
41
+ R: "rgb(23,190,207)", // "#145AFF",
42
+ N: "rgb(235,137,70)", // "#00DCDC",
43
+ D: "rgb(31,119,180)", // "#E60A0A",
44
+ C: "rgb(188,189,34)", // "#E6E600",
45
+ E: "rgb(31, 120, 150)", // "#00DCDC",
46
+ Q: "rgb(205, 111, 71)", // "#E60A0A",
47
+ G: "rgb(214,39,40)", // "#EBEBEB",
48
+ H: "rgb(158,218,229)", // "#8282D2",
49
+ I: "rgb(23,103,57)", // "#0F820F",
50
+ L: "rgb(30,110,96)", // "#0F820F",
51
+ K: "rgb(108, 218, 229)", //"#145AFF",
52
+ M: "rgb(60,131,95)", // "#E6E600",
53
+ F: "rgb(24,110,79)", // "#3232AA",
54
+ P: "rgb(255,152,150)", // "#DC9682",
55
+ S: "rgb(255,187,120)", // "#FA9600",
56
+ T: "rgb(245,167,100)", // "#FA9600",
57
+ W: "rgb(182, 223, 138)", // "#B45AB4",
58
+ Y: "rgb(152,223,138)", // "#3232AA",
59
+ V: "rgb(74,160,74)", // "#0F820F",
57
60
  },
58
61
 
59
62
  [HelmTypes.CHEM]: {
@@ -3,6 +3,7 @@ import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
 
5
5
  import wu from 'wu';
6
+ import {Observable, Subject} from 'rxjs';
6
7
 
7
8
  import {IMonomerLibBase, Monomer, RGroup} from '@datagrok-libraries/bio/src/types/index';
8
9
  import {HelmAtom, HelmType, IWebEditorMonomer, MonomerType, PolymerType} from '@datagrok-libraries/bio/src/helm/types';
@@ -10,13 +11,12 @@ import {getMonomerHandleArgs} from '@datagrok-libraries/bio/src/helm/helm-helper
10
11
  import {helmTypeToPolymerType} from '@datagrok-libraries/bio/src/monomer-works/monomer-works';
11
12
  import {HelmTypes, PolymerTypes} from '@datagrok-libraries/bio/src/helm/consts';
12
13
  import {HELM_REQUIRED_FIELD as REQ, HELM_RGROUP_FIELDS as RGP} from '@datagrok-libraries/bio/src/utils/const';
13
- import {GapOriginals, NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
14
+ import {GAP_SYMBOL, GapOriginals, NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
14
15
 
15
16
  import {AmbiguousWebEditorMonomer, GapWebEditorMonomer, MissingWebEditorMonomer} from './web-editor-monomer-dummy';
16
17
  import {LibraryWebEditorMonomer} from './web-editor-monomer-of-library';
17
18
 
18
19
  import {_package} from '../../package';
19
- import {Observable, Subject} from 'rxjs';
20
20
 
21
21
  const monomerRe = /[\w()]+/;
22
22
  //** Do not mess with monomer symbol with parenthesis enclosed in square brackets */
@@ -25,13 +25,20 @@ const ambMonomerRe = RegExp(String.raw`\(${monomerRe}(,${monomerRe})+\)`);
25
25
  export type MonomerLibDataType = { [polymerType: string]: { [monomerSymbol: string]: Monomer } };
26
26
 
27
27
  export class MonomerLibBase implements IMonomerLibBase {
28
+ protected _isEmpty: boolean;
29
+ get isEmpty(): boolean { return this._isEmpty; }
30
+
28
31
  protected _onChanged = new Subject<any>();
29
32
 
30
33
  get onChanged(): Observable<any> { return this._onChanged; }
31
34
 
35
+
32
36
  constructor(
33
37
  protected _monomers: MonomerLibDataType,
34
- ) {}
38
+ ) {
39
+ this._isEmpty = !this._monomers || Object.keys(this._monomers).length === 0 ||
40
+ Object.entries(this._monomers).every(([_, ptMonomers]) => Object.keys(ptMonomers).length === 0);
41
+ }
35
42
 
36
43
  /** Creates missing {@link Monomer} */
37
44
  addMissingMonomer(polymerType: PolymerType, monomerSymbol: string): Monomer {
@@ -40,7 +47,7 @@ export class MonomerLibBase implements IMonomerLibBase {
40
47
  mSet = this._monomers[polymerType] = {};
41
48
 
42
49
  let monomerName: string = monomerSymbol;
43
- if (monomerSymbol === GapOriginals[NOTATION.HELM])
50
+ if (monomerSymbol == GAP_SYMBOL || monomerSymbol === GapOriginals[NOTATION.HELM] /* usage from HELMWebEditor */)
44
51
  monomerName = 'Gap';
45
52
  else if (polymerType === PolymerTypes.PEPTIDE && monomerSymbol === 'X')
46
53
  monomerName = 'Any';
@@ -120,17 +127,17 @@ export class MonomerLibBase implements IMonomerLibBase {
120
127
  /** Get or create {@link org,helm.WebEditorMonomer} */
121
128
  let resWem: IWebEditorMonomer | null = m.wem ?? null;
122
129
  if (!resWem) {
123
- if (elem === '*')
124
- resWem = m.wem = new GapWebEditorMonomer(biotype, elem);
130
+ if (elem === GAP_SYMBOL || elem == '*' /* usage from HELMWebEditor */)
131
+ resWem = m.wem = new GapWebEditorMonomer(biotype);
125
132
  else if (
126
- (biotype === 'HELM_NUCLETIDE' && elem === 'N') ||
127
- (biotype === 'HELM_AA' && elem === 'X') ||
128
- (biotype === 'HELM_CHEM' && false) || // TODO: Ambiguous monomer for CHEM
133
+ (biotype === HelmTypes.NUCLEOTIDE && elem === 'N') ||
134
+ (biotype === HelmTypes.AA && elem === 'X') ||
135
+ (biotype === HelmTypes.CHEM && false) || // TODO: Ambiguous monomer for CHEM
129
136
  ambMonomerRe.test(elem) // e.g. (A,R,_)
130
137
  )
131
138
  resWem = m.wem = new AmbiguousWebEditorMonomer(biotype, elem);
132
139
  else if (!m.lib)
133
- resWem = m.wem = new MissingWebEditorMonomer(biotype, elem);
140
+ resWem = m.wem = new MissingWebEditorMonomer(biotype, elem, this.isEmpty);
134
141
 
135
142
  if (!resWem)
136
143
  resWem = m.wem = LibraryWebEditorMonomer.fromMonomer(biotype, m, this);
@@ -139,6 +146,51 @@ export class MonomerLibBase implements IMonomerLibBase {
139
146
  return resWem!;
140
147
  }
141
148
 
149
+ getTooltip(biotype: HelmType, monomerSymbol: string): HTMLElement {
150
+ const polymerType = helmTypeToPolymerType(biotype);
151
+ const res = ui.div([], {classes: 'ui-form ui-tooltip'});
152
+ const monomer = this.getMonomer(polymerType, monomerSymbol);
153
+ if (monomer) {
154
+ // Symbol & Name
155
+ const symbol = monomer[REQ.SYMBOL];
156
+ const _name = monomer[REQ.NAME];
157
+ const wem = this.getWebEditorMonomer(biotype, monomerSymbol)!;
158
+ const htmlColor = wem.backgroundcolor;
159
+ res.append(ui.divH([
160
+ ui.div([symbol], {style: {fontWeight: 'bolder', textWrap: 'nowrap', marginRight: '6px', color: htmlColor}}),
161
+ ui.div([monomer.name])
162
+ ], {style: {display: 'flex', flexDirection: 'row', justifyContent: 'left'}}));
163
+
164
+ // Structure
165
+ const chemOptions = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
166
+ let structureEl: HTMLElement;
167
+ if (monomer.molfile)
168
+ structureEl = grok.chem.svgMol(monomer.molfile, undefined, undefined, chemOptions);
169
+ else if (monomer.smiles) {
170
+ structureEl = ui.divV([
171
+ grok.chem.svgMol(monomer.smiles, undefined, undefined, chemOptions),
172
+ ui.divText('from smiles', {style: {fontSize: 'smaller'}}),
173
+ ]);
174
+ } else {
175
+ // Unable to get monomer's structure
176
+ structureEl = ui.divText('No structure', {style: {margin: '6px'}});
177
+ }
178
+ res.append(ui.div(structureEl,
179
+ {style: {display: 'flex', flexDirection: 'row', justifyContent: 'center', margin: '6px'}}));
180
+
181
+ // Source
182
+ if (monomer.symbol != GAP_SYMBOL)
183
+ res.append(ui.divText(monomer.lib?.source ?? 'Missed in libraries'));
184
+ } else {
185
+ res.append(ui.divV([
186
+ ui.divText(`Monomer '${monomerSymbol}' of type '${polymerType}' not found.`),
187
+ ui.divText('Open the Context Panel, then expand Manage Libraries'),
188
+ ]));
189
+ }
190
+ return res;
191
+ }
192
+
193
+
142
194
  getRS(smiles: string): { [r: string]: string } {
143
195
  const newS = smiles.match(/(?<=\[)[^\][]*(?=])/gm);
144
196
  const res: { [name: string]: string } = {};
@@ -163,22 +163,20 @@ export class MonomerLib extends MonomerLibBase implements IMonomerLib {
163
163
  this._monomers[type][monomerSymbol] = lib.getMonomer(type, monomerSymbol)!;
164
164
  });
165
165
  });
166
- }
167
-
168
- public update(lib: IMonomerLib): void {
169
- this._updateLibInt(lib);
170
- this._onChanged.next();
166
+ this._isEmpty = this.isEmpty && lib.isEmpty;
171
167
  }
172
168
 
173
169
  public updateLibs(libList: IMonomerLib[], reload: boolean = false): void {
174
- if (reload)
170
+ if (reload) {
175
171
  this._monomers = {};
172
+ this._isEmpty = true;
173
+ }
176
174
  this._duplicateMonomers = {}; // Reset duplicates
177
175
  for (const lib of libList)
178
176
  if (!lib.error) this._updateLibInt(lib);
179
177
  if (Object.entries(this.duplicateMonomers).length > 0) {
180
178
  getUserLibSettings().then((settings) => {
181
- this.assignDuplicatePreferances(settings);
179
+ this.assignDuplicatePreferences(settings);
182
180
  });
183
181
  } else
184
182
  this._duplicatesHandled = true;
@@ -186,8 +184,8 @@ export class MonomerLib extends MonomerLibBase implements IMonomerLib {
186
184
  this._onChanged.next();
187
185
  }
188
186
 
189
- /** Checks wether all duplicated monomers have set preferences in user settings. overwrites those which have. */
190
- assignDuplicatePreferances(userSettings: UserLibSettings): boolean {
187
+ /** Checks weather all duplicated monomers have set preferences in user settings. overwrites those which have. */
188
+ assignDuplicatePreferences(userSettings: UserLibSettings): boolean {
191
189
  let res = true;
192
190
  for (const polymerType in this.duplicateMonomers) {
193
191
  for (const monomerSymbol in this.duplicateMonomers[polymerType]) {
@@ -243,47 +241,6 @@ export class MonomerLib extends MonomerLibBase implements IMonomerLib {
243
241
  return resStr;
244
242
  }
245
243
 
246
- getTooltip(biotype: HelmType, monomerSymbol: string): HTMLElement {
247
- const polymerType = helmTypeToPolymerType(biotype);
248
- const res = ui.div([], {classes: 'ui-form ui-tooltip'});
249
- const monomer = this.getMonomer(polymerType, monomerSymbol);
250
- if (monomer) {
251
- // Symbol & Name
252
- const symbol = monomer[REQ.SYMBOL];
253
- const _name = monomer[REQ.NAME];
254
- res.append(ui.divH([
255
- ui.div([symbol], {style: {fontWeight: 'bolder', textWrap: 'nowrap', marginRight: '6px'}}),
256
- ui.div([monomer.name])
257
- ], {style: {display: 'flex', flexDirection: 'row', justifyContent: 'left'}}));
258
-
259
- // Structure
260
- const chemOptions = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
261
- let structureEl: HTMLElement;
262
- if (monomer.molfile)
263
- structureEl = grok.chem.svgMol(monomer.molfile, undefined, undefined, chemOptions);
264
- else if (monomer.smiles) {
265
- structureEl = ui.divV([
266
- grok.chem.svgMol(monomer.smiles, undefined, undefined, chemOptions),
267
- ui.divText('from smiles', {style: {fontSize: 'smaller'}}),
268
- ]);
269
- } else {
270
- // Unable to get monomer's structure
271
- structureEl = ui.divText('No structure', {style: {margin: '6px'}});
272
- }
273
- res.append(ui.div(structureEl,
274
- {style: {display: 'flex', flexDirection: 'row', justifyContent: 'center', margin: '6px'}}));
275
-
276
- // Source
277
- res.append(ui.divText(monomer.lib?.source ?? 'Missed in libraries'));
278
- } else {
279
- res.append(ui.divV([
280
- ui.divText(`Monomer '${monomerSymbol}' of type '${polymerType}' not found.`),
281
- ui.divText('Open the Context Panel, then expand Manage Libraries'),
282
- ]));
283
- }
284
- return res;
285
- }
286
-
287
244
  override(data: MonomerLibData): IMonomerLibBase {
288
245
  return new OverriddenMonomerLib(data, this);
289
246
  }
@@ -3,7 +3,7 @@ import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
 
5
5
  import {
6
- HelmType, PolymerType, MonomerType,
6
+ PolymerType, MonomerType,
7
7
  IWebEditorMonomer, WebEditorRGroups
8
8
  } from '@datagrok-libraries/bio/src/helm/types';
9
9
 
@@ -85,8 +85,9 @@ export class GapWebEditorMonomer extends WebEditorMonomerDummy {
85
85
  public readonly linecolor: string = '#808080';
86
86
  public readonly textcolor: string = '#808080';
87
87
 
88
- constructor(biotype: string, id: string) {
89
- super(biotype, id, 'gap');
88
+ constructor(biotype: string) {
89
+ // monomer symbol is used to build pseudo molfile for Helm, symbol can not be empty for molfile
90
+ super(biotype, '*', 'gap');
90
91
  }
91
92
  }
92
93
 
@@ -105,8 +106,14 @@ export class MissingWebEditorMonomer extends WebEditorMonomerDummy {
105
106
  public readonly linecolor: string = '#800000';
106
107
  public readonly textcolor: string = '#FFFFFF';
107
108
 
108
- constructor(biotype: string, id: string) {
109
+ constructor(biotype: string, id: string, isLibEmpty: boolean) {
109
110
  super(biotype, id, 'missing');
111
+
112
+ if (isLibEmpty) {
113
+ this.backgroundcolor = '#C0C0C0';
114
+ this.linecolor = '#404040';
115
+ this.textcolor = '#404040';
116
+ }
110
117
  }
111
118
  }
112
119
 
@@ -43,7 +43,8 @@ export class LibraryWebEditorMonomer implements IWebEditorMonomer {
43
43
  at = monomerLib.getRS(smiles);
44
44
  } else if (!monomer.lib) {
45
45
  // missing
46
- return new MissingWebEditorMonomer(biotype, symbol);
46
+ throw new Error('Unexpected missing monomer without .lib');
47
+ // return new MissingWebEditorMonomer(biotype, symbol);
47
48
  } else {
48
49
  // broken
49
50
  return new BrokenWebEditorMonomer(biotype, symbol);
@@ -58,7 +59,7 @@ export class LibraryWebEditorMonomer implements IWebEditorMonomer {
58
59
  monomer[REQ.MONOMER_TYPE],
59
60
  at);
60
61
 
61
- const colors = getMonomerColors(biotype, monomer);
62
+ const colors = getMonomerColors(biotype, monomer, monomerLib);
62
63
  if (colors) {
63
64
  res.textcolor = colors?.textcolor;
64
65
  res.linecolor = colors?.linecolor;
@@ -69,7 +70,7 @@ export class LibraryWebEditorMonomer implements IWebEditorMonomer {
69
70
  }
70
71
  }
71
72
 
72
- function getMonomerColors(biotype: HelmType, monomer: Monomer): IMonomerColors | null {
73
+ function getMonomerColors(biotype: HelmType, monomer: Monomer, monomerLib?: IMonomerLibBase): IMonomerColors | null {
73
74
  const currentMonomerSchema = 'default';
74
75
  let monomerSchema: string = currentMonomerSchema;
75
76
 
@@ -77,23 +78,29 @@ function getMonomerColors(biotype: HelmType, monomer: Monomer): IMonomerColors |
77
78
  if (monomer.meta && monomer.meta.colors) {
78
79
  const monomerColors: { [colorSchemaName: string]: any } = monomer.meta.colors;
79
80
  if (!(currentMonomerSchema in monomerColors)) monomerSchema = 'default';
80
- let res = monomerColors[monomerSchema];
81
+ res = monomerColors[monomerSchema];
81
82
  }
82
83
 
83
84
  if (!res) {
84
85
  const biotypeColors: { [symbol: string]: string } | undefined = naturalMonomerColors[biotype];
85
- const nColor = biotypeColors?.[monomer.symbol];
86
- if (nColor)
87
- res = {textColor: "#000000", lineColor: "#000000", backgroundColor: nColor};
86
+ const nColor: string = biotypeColors?.[monomer.symbol];
87
+ if (nColor) {
88
+ const nTextColor = DG.Color.toHtml(DG.Color.getContrastColor(DG.Color.fromHtml(nColor)));
89
+ res = {textColor: nTextColor, lineColor: '#202020', backgroundColor: nColor};
90
+ }
88
91
  }
89
92
 
90
- const na = monomer[OPT.NATURAL_ANALOG];
91
- if (!res && na) {
92
- const biotypeColors: { [symbol: string]: string } | undefined = naturalMonomerColors[biotype];
93
- const naColor = biotypeColors?.[na];
94
- res = {textColor: "#000000", lineColor: "#000000", backgroundColor: naColor ?? "#FFFFFF",};
93
+ const naSymbol: string | undefined = monomer[OPT.NATURAL_ANALOG];
94
+ if (!res && naSymbol) {
95
+ const polymerType = monomer[REQ.POLYMER_TYPE];
96
+ const naMonomer = monomerLib?.getMonomer(polymerType, naSymbol);
97
+ if (naMonomer)
98
+ return getMonomerColors(biotype, naMonomer);
95
99
  }
96
100
 
101
+ if (!res)
102
+ res = {textColor: "#202020", lineColor: "#202020", backgroundColor: "#A0A0A0"};
103
+
97
104
  return !res ? null : {
98
105
  textcolor: res.text ?? res.textColor,
99
106
  linecolor: res.line ?? res.lineColor,
@@ -7,9 +7,8 @@ import wu from 'wu';
7
7
  import {fromEvent, Observable, Subject, Unsubscribable} from 'rxjs';
8
8
 
9
9
  import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
10
- import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
11
10
  import {
12
- monomerToShort, pickUpPalette, pickUpSeqCol, TAGS as bioTAGS, positionSeparator
11
+ monomerToShort, pickUpSeqCol, TAGS as bioTAGS, positionSeparator, ALPHABET
13
12
  } from '@datagrok-libraries/bio/src/utils/macromolecule';
14
13
  import {
15
14
  FilterSources, HorizontalAlignments, IWebLogoViewer, PositionHeight, PositionMarginStates,
@@ -21,11 +20,14 @@ import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/type
21
20
  import {testEvent} from '@datagrok-libraries/utils/src/test';
22
21
  import {PromiseSyncer} from '@datagrok-libraries/bio/src/utils/syncer';
23
22
  import {GAP_SYMBOL} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
23
+ import {IMonomerLibBase} from '@datagrok-libraries/bio/src/types/index';
24
+ import {HelmType} from '@datagrok-libraries/bio/src/helm/types';
25
+ import {undefinedColor} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
24
26
 
25
27
  import {AggFunc, getAgg} from '../utils/agg';
26
28
  import {buildCompositionTable} from '../widgets/composition-analysis-widget';
27
29
 
28
- import {_package} from '../package';
30
+ import {_package, getMonomerLibHelper} from '../package';
29
31
 
30
32
  declare global {
31
33
  interface HTMLCanvasElement {
@@ -199,7 +201,8 @@ export class PositionInfo {
199
201
  }
200
202
 
201
203
  render(g: CanvasRenderingContext2D,
202
- fontStyle: string, uppercaseLetterAscent: number, uppercaseLetterHeight: number, cp: SeqPalette
204
+ fontStyle: string, uppercaseLetterAscent: number, uppercaseLetterHeight: number,
205
+ biotype: HelmType, monomerLib: IMonomerLibBase | null
203
206
  ) {
204
207
  for (const [monomer, pmInfo] of Object.entries(this._freqs)) {
205
208
  if (monomer !== GAP_SYMBOL) {
@@ -207,11 +210,17 @@ export class PositionInfo {
207
210
  const b = pmInfo.bounds!;
208
211
  const left = b.left;
209
212
 
213
+ let color: string = undefinedColor;
214
+ if (monomerLib) {
215
+ const wem = monomerLib.getWebEditorMonomer(biotype, monomer)!;
216
+ color = wem.backgroundcolor!;
217
+ }
218
+
210
219
  g.resetTransform();
211
220
  g.strokeStyle = 'lightgray';
212
221
  g.lineWidth = 1;
213
222
  g.rect(left, b.top, b.width, b.height);
214
- g.fillStyle = cp.get(monomer) ?? cp.get('other');
223
+ g.fillStyle = color;
215
224
  g.textAlign = 'left';
216
225
  g.font = fontStyle;
217
226
  //g.fillRect(b.left, b.top, b.width, b.height);
@@ -233,13 +242,13 @@ export class PositionInfo {
233
242
  return !!findRes ? findRes[0] : undefined;
234
243
  }
235
244
 
236
- buildCompositionTable(palette: SeqPalette): HTMLTableElement {
245
+ buildCompositionTable(biotype: HelmType, monomerLib: IMonomerLibBase): HTMLTableElement {
237
246
  if ('-' in this._freqs)
238
247
  throw new Error(`Unexpected monomer symbol '-'.`);
239
- return buildCompositionTable(palette,
248
+ return buildCompositionTable(
240
249
  Object.assign({}, ...Object.entries(this._freqs)
241
- .map(([m, pmi]) => ({[m]: pmi.rowCount})))
242
- );
250
+ .map(([m, pmi]) => ({[m]: pmi.rowCount}))),
251
+ biotype, monomerLib);
243
252
  }
244
253
  }
245
254
 
@@ -304,9 +313,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
304
313
  private seqHandler: SeqHandler | null;
305
314
  private initialized: boolean = false;
306
315
 
307
- // private readonly colorScheme: ColorScheme = ColorSchemes[NucleotidesWebLogo.residuesSet];
308
- protected palette: SeqPalette | null = null;
309
-
316
+ private monomerLib: IMonomerLibBase | null = null;
310
317
  private host?: HTMLDivElement;
311
318
  private msgHost?: HTMLElement;
312
319
  private canvas: HTMLCanvasElement;
@@ -446,6 +453,14 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
446
453
  this.canvas.classList.value = 'bio-wl-canvas';
447
454
  this.canvas.style.width = '100%';
448
455
 
456
+ getMonomerLibHelper().then((libHelper) => {
457
+ this.monomerLib = libHelper.getMonomerLib();
458
+ this.render(WlRenderLevel.Render, 'monomerLib');
459
+ this.subs.push(this.monomerLib.onChanged.subscribe(() => {
460
+ this.render(WlRenderLevel.Render, 'monomerLib changed');
461
+ }));
462
+ });
463
+
449
464
  /* this.root.style.background = '#FFEEDD'; */
450
465
  this.viewSyncer = new PromiseSyncer(_package.logger);
451
466
  }
@@ -583,7 +598,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
583
598
  // Set valueColumnNameProp.choices has no effect
584
599
  }
585
600
 
586
- /** Assigns {@link seqCol} and {@link palette} based on {@link sequenceColumnName} and calls {@link render}(). */
601
+ /** Assigns {@link seqCol} based on {@link sequenceColumnName} and calls {@link render}(). */
587
602
  private updateSeqCol(): void {
588
603
  if (this.dataFrame) {
589
604
  this.seqCol = this.sequenceColumnName ? this.dataFrame.col(this.sequenceColumnName) : null;
@@ -595,7 +610,6 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
595
610
  try {
596
611
  this.seqHandler = SeqHandler.forColumn(this.seqCol);
597
612
 
598
- this.palette = pickUpPalette(this.seqCol);
599
613
  this.render(WlRenderLevel.Freqs, 'updateSeqCol()');
600
614
  this.error = null;
601
615
  } catch (err: any) {
@@ -609,7 +623,6 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
609
623
  this.positionLabels = [];
610
624
  this.startPosition = -1;
611
625
  this.endPosition = -1;
612
- this.palette = null;
613
626
  }
614
627
  }
615
628
  }
@@ -1085,15 +1098,10 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
1085
1098
  this._onLayoutCalculated.next();
1086
1099
  };
1087
1100
 
1088
- if (this.msgHost) {
1089
- if (this.seqCol && !this.palette) {
1090
- this.msgHost!.innerText = `Unknown palette (column semType: '${this.seqCol.semType}').`;
1091
- this.msgHost!.style.display = '';
1092
- } else
1093
- this.msgHost!.style.display = 'none';
1094
- }
1101
+ if (this.msgHost)
1102
+ this.msgHost!.style.display = 'none';
1095
1103
 
1096
- if (!this.seqCol || !this.dataFrame || !this.palette || this.host == null || this.slider == null)
1104
+ if (!this.seqCol || !this.dataFrame || this.host == null || this.slider == null)
1097
1105
  return;
1098
1106
 
1099
1107
  const dpr: number = window.devicePixelRatio;
@@ -1133,8 +1141,10 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
1133
1141
  // Hacks to scale uppercase characters to target rectangle
1134
1142
  const uppercaseLetterAscent = 0.25;
1135
1143
  const uppercaseLetterHeight = 12.2;
1144
+ const sh = SeqHandler.forColumn(this.seqCol);
1145
+ const biotype = sh.defaultBiotype;
1136
1146
  for (let jPos = firstPos; jPos <= lastPos; jPos++)
1137
- this.positions[jPos].render(g, fontStyle, uppercaseLetterAscent, uppercaseLetterHeight, this.palette);
1147
+ this.positions[jPos].render(g, fontStyle, uppercaseLetterAscent, uppercaseLetterHeight, biotype, this.monomerLib);
1138
1148
  } finally {
1139
1149
  g.restore();
1140
1150
  }
@@ -1216,12 +1226,15 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
1216
1226
  const [pi, monomer] = this.getMonomer(cursorP, dpr);
1217
1227
  const positionLabelHeight = this.showPositionLabels ? POSITION_LABELS_HEIGHT * dpr : 0;
1218
1228
 
1219
- if (pi !== null && monomer === null && 0 <= cursorP.y && cursorP.y <= positionLabelHeight) {
1229
+ if (pi !== null && monomer === null && 0 <= cursorP.y && cursorP.y <= positionLabelHeight && this.monomerLib) {
1220
1230
  // Position tooltip
1221
1231
 
1222
1232
  const tooltipRows = [ui.divText(`Position ${pi.label}`)];
1223
- if (this.valueAggrType === DG.AGG.TOTAL_COUNT)
1224
- tooltipRows.push(pi.buildCompositionTable(this.palette!));
1233
+ if (this.valueAggrType === DG.AGG.TOTAL_COUNT) {
1234
+ const sh = SeqHandler.forColumn(this.seqCol!);
1235
+ const biotype = sh.defaultBiotype;
1236
+ tooltipRows.push(pi.buildCompositionTable(biotype, this.monomerLib));
1237
+ }
1225
1238
  const tooltipEl = ui.divV(tooltipRows);
1226
1239
  ui.tooltip.show(tooltipEl, args.x + 16, args.y + 16);
1227
1240
  } else if (pi !== null && monomer && this.dataFrame && this.seqCol && this.seqHandler) {
@@ -7,27 +7,19 @@ import wu from 'wu';
7
7
  import {TAGS as bioTAGS, ALPHABET, getPaletteByType} from '@datagrok-libraries/bio/src/utils/macromolecule';
8
8
  import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
9
9
  import {UnknownSeqPalettes} from '@datagrok-libraries/bio/src/unknown';
10
- import '../../css/composition-analysis.css';
11
10
  import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
12
11
  import {GAP_SYMBOL} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
12
+ import {IMonomerLibBase} from '@datagrok-libraries/bio/src/types';
13
+ import {HelmType} from '@datagrok-libraries/bio/src/helm/types';
14
+ import {HelmTypes} from '@datagrok-libraries/bio/src/helm/consts';
13
15
 
16
+ import '../../css/composition-analysis.css';
14
17
 
15
- export function getCompositionAnalysisWidget(val: DG.SemanticValue): DG.Widget {
18
+ export function getCompositionAnalysisWidget(val: DG.SemanticValue, monomerLib: IMonomerLibBase): DG.Widget {
16
19
  const host = ui.div();
17
20
  host.classList.add('macromolecule-cell-comp-analysis-host');
18
21
  const alphabet = val.cell.column.tags[bioTAGS.alphabet];
19
- let palette: SeqPalette = UnknownSeqPalettes.Color;
20
- switch (alphabet) {
21
- case ALPHABET.DNA:
22
- case ALPHABET.RNA:
23
- palette = getPaletteByType(ALPHABET.DNA);
24
- break;
25
- case ALPHABET.PT:
26
- palette = getPaletteByType(ALPHABET.PT);
27
- break;
28
- default:
29
- break;
30
- }
22
+ const biotype = alphabet === ALPHABET.DNA || alphabet === ALPHABET.RNA ? HelmTypes.NUCLEOTIDE : HelmTypes.AA;
31
23
 
32
24
  const counts: { [m: string]: number } = {};
33
25
  const sh = SeqHandler.forColumn(val.cell.column as DG.Column<string>);
@@ -38,7 +30,7 @@ export function getCompositionAnalysisWidget(val: DG.SemanticValue): DG.Widget {
38
30
  const count = counts[cm] || 0;
39
31
  counts[cm] = count + 1;
40
32
  });
41
- const table = buildCompositionTable(palette, counts);
33
+ const table = buildCompositionTable(counts, biotype, monomerLib);
42
34
  Array.from(table.rows).forEach((row) => {
43
35
  const barCol = (row.getElementsByClassName('macromolecule-cell-comp-analysis-bar')[0] as HTMLDivElement)
44
36
  .style.backgroundColor;
@@ -49,7 +41,9 @@ export function getCompositionAnalysisWidget(val: DG.SemanticValue): DG.Widget {
49
41
  return new DG.Widget(host);
50
42
  }
51
43
 
52
- export function buildCompositionTable(palette: SeqPalette, counts: { [m: string]: number }): HTMLTableElement {
44
+ export function buildCompositionTable(
45
+ counts: { [m: string]: number }, biotype: HelmType, monomerLib: IMonomerLibBase
46
+ ): HTMLTableElement {
53
47
  let sumValue: number = 0;
54
48
  let maxValue: number | null = null;
55
49
  for (const value of Object.values(counts)) {
@@ -61,7 +55,8 @@ export function buildCompositionTable(palette: SeqPalette, counts: { [m: string]
61
55
  .sort((a, b) => b[1] - a[1])
62
56
  .map(([cm, value]) => {
63
57
  const ratio = value / sumValue;
64
- const color = palette.get(cm);
58
+ const wem = monomerLib.getWebEditorMonomer(biotype, cm)!;
59
+ const color = wem.backgroundcolor!;
65
60
  const barDiv = ui.div('', {classes: 'macromolecule-cell-comp-analysis-bar'});
66
61
  barDiv.style.width = `${50 * ratio / maxRatio}px`;
67
62
  barDiv.style.backgroundColor = color;