@datagrok/bio 2.15.7 → 2.15.9

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.
@@ -1,15 +1,15 @@
1
- import * as grok from 'datagrok-api/grok';
2
1
  import * as DG from 'datagrok-api/dg';
2
+ import {GridCell} from 'datagrok-api/dg';
3
3
  import * as ui from 'datagrok-api/ui';
4
-
5
- import {PolymerType} from '@datagrok-libraries/bio/src/helm/types';
6
- import {ALPHABET, getPaletteByType, monomerToShort} from '@datagrok-libraries/bio/src/utils/macromolecule';
7
- import {TAGS as bioTAGS, } from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
4
+ import {ALPHABET, monomerToShort} from '@datagrok-libraries/bio/src/utils/macromolecule';
5
+ import {GAP_SYMBOL, TAGS as bioTAGS,} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
8
6
  import {MONOMER_RENDERER_TAGS} from '@datagrok-libraries/bio/src/utils/cell-renderer';
9
- import {MonomerWorks} from '@datagrok-libraries/bio/src/monomer-works/monomer-works';
7
+ import {getGridCellColTemp} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
10
8
 
9
+ import {CellRendererWithMonomerLibBackBase} from './monomer-cell-renderer-base';
11
10
  import * as C from './constants';
12
- import {getMonomerLib} from '../package';
11
+ import {undefinedColor} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
12
+ import {HelmTypes} from '@datagrok-libraries/js-draw-lite/src/types/org';
13
13
 
14
14
  const Tags = new class {
15
15
  tooltipHandlerTemp = 'tooltip-handler.Monomer';
@@ -17,17 +17,50 @@ const Tags = new class {
17
17
 
18
18
  const svgMolOptions = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
19
19
 
20
- export class MonomerTooltipHandler {
21
- private readonly gridCol: DG.GridColumn;
20
+ export class MonomerCellRendererBack extends CellRendererWithMonomerLibBackBase {
21
+ constructor(gridCol: DG.GridColumn | null, tableCol: DG.Column) {
22
+ super(gridCol, tableCol);
23
+ }
22
24
 
23
- constructor(gridCol: DG.GridColumn) {
24
- this.gridCol = gridCol;
25
- this.gridCol.grid.onCellTooltip(this.onCellTooltip.bind(this));
25
+ render(g: CanvasRenderingContext2D,
26
+ x: number, y: number, w: number, h: number, gridCell: DG.GridCell, cellStyle: DG.GridCellStyle
27
+ ): void {
28
+ g.save();
29
+ try {
30
+ if (gridCell.gridRow < 0) return;
31
+ const applyToBackground = gridCell.cell?.column && gridCell.cell.column.getTag(MONOMER_RENDERER_TAGS.applyToBackground) === 'true';
32
+
33
+ g.font = `12px monospace`;
34
+ g.textBaseline = 'middle';
35
+ g.textAlign = 'center';
36
+
37
+ const symbol: string = gridCell.cell.value;
38
+ if (!symbol || symbol == GAP_SYMBOL) return;
39
+
40
+ let color = undefinedColor;
41
+ if (this.monomerLib) {
42
+ const alphabet = this.tableCol.getTag(bioTAGS.alphabet);
43
+ const biotype = alphabet === ALPHABET.RNA || alphabet === ALPHABET.DNA ? HelmTypes.NUCLEOTIDE : HelmTypes.AA;
44
+ color = this.monomerLib.getMonomerTextColor(biotype, symbol);
45
+ }
46
+
47
+ //cell width of monomer should dictate how many characters can be displayed
48
+ // for width 40, 6 characters can be displayed (0.15 is 6 / 40)
49
+ const maxChars = Math.max(2, Math.floor(w * 0.15));
50
+ g.fillStyle = color;
51
+ if (applyToBackground) {
52
+ g.fillRect(x, y, w, h);
53
+ g.fillStyle = DG.Color.toHtml(DG.Color.getContrastColor(DG.Color.fromHtml(color)));
54
+ }
55
+ g.fillText(monomerToShort(symbol, maxChars), x + (w / 2), y + (h / 2), w);
56
+ } finally {
57
+ g.restore();
58
+ }
26
59
  }
27
60
 
28
- private onCellTooltip(gridCell: DG.GridCell, x: number, y: number): any {
61
+ override onMouseMove(gridCell: GridCell, e: MouseEvent) {
29
62
  if (
30
- gridCell.grid.dart != this.gridCol.grid.dart || gridCell.gridColumn.dart != this.gridCol.dart ||
63
+ gridCell.grid.dart != this.gridCol?.grid.dart || gridCell.gridColumn.dart != this.gridCol?.dart ||
31
64
  !gridCell.tableColumn || !gridCell.isTableCell
32
65
  ) return false;
33
66
 
@@ -37,32 +70,33 @@ export class MonomerTooltipHandler {
37
70
  const x1 = gridCell.bounds.right + canvasClientRect.left - 4;
38
71
  const y1 = gridCell.bounds.bottom + canvasClientRect.top - 4;
39
72
 
40
- const monomerLib = getMonomerLib();
41
- if (!monomerLib) {
42
- ui.tooltip.show(ui.divText('Monomer library is not available.'), x1, y1);
73
+ if (monomerName == GAP_SYMBOL) {
74
+ ui.tooltip.show(ui.divText('gap'), x1, y1);
43
75
  return true;
44
76
  }
45
77
 
46
- const mw = new MonomerWorks(monomerLib);
47
- const polymerType: PolymerType = (alphabet === ALPHABET.DNA || alphabet === ALPHABET.RNA) ? 'RNA' :
48
- alphabet === ALPHABET.PT ? 'PEPTIDE' : 'PEPTIDE';
49
- // const biotype: HelmType = [ALPHABET.RNA, ALPHABET.DNA].includes(alphabet) ? HelmTypes.NUCLEOTIDE :
50
- // [ALPHABET.PT].includes(alphabet) ? HelmTypes.AA : HelmTypes.AA;
51
-
52
- const monomerMol: string | null = mw.getCappedRotatedMonomer(polymerType, monomerName);
53
- const nameDiv = ui.div(monomerName);
54
- const molDiv = !monomerMol ? null :
55
- grok.chem.svgMol(monomerMol, undefined, undefined, svgMolOptions);
78
+ if (!this.monomerLib) {
79
+ ui.tooltip.show(ui.divText('Monomer library is not available.'), x1, y1);
80
+ return true;
81
+ }
56
82
 
57
- ui.tooltip.show(ui.divV([nameDiv, ...(molDiv ? [molDiv] : [])]), x1, y1);
83
+ const biotype = alphabet === ALPHABET.RNA || alphabet === ALPHABET.DNA ? HelmTypes.NUCLEOTIDE : HelmTypes.AA;
84
+ const tooltipEl = this.monomerLib.getTooltip(biotype, monomerName);
85
+ ui.tooltip.show(tooltipEl, x1, y1);
58
86
 
59
87
  return true; // To prevent default tooltip behaviour
60
88
  }
61
89
 
62
- public static getOrCreate(gridCol: DG.GridColumn): MonomerTooltipHandler {
63
- let res = gridCol.temp[Tags.tooltipHandlerTemp];
64
- if (!res)
65
- res = gridCol.temp[Tags.tooltipHandlerTemp] = new MonomerTooltipHandler(gridCol);
90
+ override async awaitRendered(timeout: number = 10000, reason: string = `${timeout} timeout`): Promise<void> {
91
+ return Promise.resolve();
92
+ }
93
+
94
+ static getOrCreate(gridCell: DG.GridCell): MonomerCellRendererBack {
95
+ const [gridCol, tableCol, temp] =
96
+ getGridCellColTemp<string, MonomerCellRendererBack>(gridCell);
97
+
98
+ let res: MonomerCellRendererBack = temp.rendererBack;
99
+ if (!res) res = temp.rendererBack = new MonomerCellRendererBack(gridCol, tableCol);
66
100
  return res;
67
101
  }
68
102
  }
@@ -91,27 +125,12 @@ export class MonomerCellRenderer extends DG.GridCellRenderer {
91
125
  g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, gridCell: DG.GridCell,
92
126
  _cellStyle: DG.GridCellStyle
93
127
  ): void {
94
- if (gridCell.gridRow < 0) return;
95
- MonomerTooltipHandler.getOrCreate(gridCell.gridColumn);
96
- const applyToBackground = gridCell.cell?.column && gridCell.cell.column.getTag(MONOMER_RENDERER_TAGS.applyToBackground) === 'true';
97
-
98
- g.font = `12px monospace`;
99
- g.textBaseline = 'middle';
100
- g.textAlign = 'center';
101
-
102
- const palette = getPaletteByType(gridCell.cell.column.getTag(bioTAGS.alphabet));
103
- const s: string = gridCell.cell.value;
104
- if (!s)
105
- return;
106
- const color = palette.get(s);
107
- //cell width of monomer should dictate how many characters can be displayed
108
- // for width 40, 6 characters can be displayed (0.15 is 6 / 40)
109
- const maxChars = Math.max(2, Math.floor(w * 0.15));
110
- g.fillStyle = color;
111
- if (applyToBackground) {
112
- g.fillRect(x, y, w, h);
113
- g.fillStyle = DG.Color.toHtml(DG.Color.getContrastColor(DG.Color.fromHtml(color)));
114
- }
115
- g.fillText(monomerToShort(s, maxChars), x + (w / 2), y + (h / 2), w);
128
+ const back = MonomerCellRendererBack.getOrCreate(gridCell);
129
+ back.render(g, x, y, w, h, gridCell, _cellStyle);
130
+ }
131
+
132
+ onMouseMove(gridCell: GridCell, e: MouseEvent) {
133
+ const back = MonomerCellRendererBack.getOrCreate(gridCell);
134
+ back.onMouseMove(gridCell, e);
116
135
  }
117
136
  }
@@ -95,7 +95,7 @@ export class MonomerLibManager implements IMonomerLibHelper {
95
95
  }
96
96
 
97
97
  assignDuplicatePreferances(settings: UserLibSettings) {
98
- this._monomerLib.assignDuplicatePreferances(settings);
98
+ this._monomerLib.assignDuplicatePreferences(settings);
99
99
  }
100
100
 
101
101
  /** Instance promise of {@link getFileManager} */
@@ -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,20 +3,23 @@ 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
- import {HelmAtom, HelmType, IWebEditorMonomer, MonomerType, PolymerType} from '@datagrok-libraries/bio/src/helm/types';
9
+ import {HelmAtom, HelmType, IMonomerColors, IWebEditorMonomer, MonomerType, PolymerType} from '@datagrok-libraries/bio/src/helm/types';
9
10
  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
- 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';
13
+ import {HELM_OPTIONAL_FIELDS as OPT, HELM_REQUIRED_FIELD as REQ, HELM_RGROUP_FIELDS as RGP} from '@datagrok-libraries/bio/src/utils/const';
14
+ import {GAP_SYMBOL, GapOriginals, NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
15
+ import {Vector} from '@datagrok-libraries/utils/src/type-declarations';
16
+ import {vectorAdd, vectorDotProduct, vectorLength} from '@datagrok-libraries/utils/src/vector-operations';
14
17
 
15
18
  import {AmbiguousWebEditorMonomer, GapWebEditorMonomer, MissingWebEditorMonomer} from './web-editor-monomer-dummy';
16
19
  import {LibraryWebEditorMonomer} from './web-editor-monomer-of-library';
20
+ import {naturalMonomerColors} from './monomer-colors';
17
21
 
18
22
  import {_package} from '../../package';
19
- import {Observable, Subject} from 'rxjs';
20
23
 
21
24
  const monomerRe = /[\w()]+/;
22
25
  //** Do not mess with monomer symbol with parenthesis enclosed in square brackets */
@@ -24,14 +27,25 @@ const ambMonomerRe = RegExp(String.raw`\(${monomerRe}(,${monomerRe})+\)`);
24
27
 
25
28
  export type MonomerLibDataType = { [polymerType: string]: { [monomerSymbol: string]: Monomer } };
26
29
 
30
+ const whiteColorV = new Vector([255.0, 255.0, 255.0]);
31
+ const blackColorV = new Vector([0.0, 0.0, 0.0]);
32
+ const maxTextColorVLen = vectorLength(whiteColorV) * 0.7;
33
+
27
34
  export class MonomerLibBase implements IMonomerLibBase {
35
+ protected _isEmpty: boolean;
36
+ get isEmpty(): boolean { return this._isEmpty; }
37
+
28
38
  protected _onChanged = new Subject<any>();
29
39
 
30
40
  get onChanged(): Observable<any> { return this._onChanged; }
31
41
 
42
+
32
43
  constructor(
33
44
  protected _monomers: MonomerLibDataType,
34
- ) {}
45
+ ) {
46
+ this._isEmpty = !this._monomers || Object.keys(this._monomers).length === 0 ||
47
+ Object.entries(this._monomers).every(([_, ptMonomers]) => Object.keys(ptMonomers).length === 0);
48
+ }
35
49
 
36
50
  /** Creates missing {@link Monomer} */
37
51
  addMissingMonomer(polymerType: PolymerType, monomerSymbol: string): Monomer {
@@ -40,7 +54,7 @@ export class MonomerLibBase implements IMonomerLibBase {
40
54
  mSet = this._monomers[polymerType] = {};
41
55
 
42
56
  let monomerName: string = monomerSymbol;
43
- if (monomerSymbol === GapOriginals[NOTATION.HELM])
57
+ if (monomerSymbol == GAP_SYMBOL || monomerSymbol === GapOriginals[NOTATION.HELM] /* usage from HELMWebEditor */)
44
58
  monomerName = 'Gap';
45
59
  else if (polymerType === PolymerTypes.PEPTIDE && monomerSymbol === 'X')
46
60
  monomerName = 'Any';
@@ -120,23 +134,147 @@ export class MonomerLibBase implements IMonomerLibBase {
120
134
  /** Get or create {@link org,helm.WebEditorMonomer} */
121
135
  let resWem: IWebEditorMonomer | null = m.wem ?? null;
122
136
  if (!resWem) {
123
- if (elem === '*')
124
- resWem = m.wem = new GapWebEditorMonomer(biotype, elem);
137
+ if (elem === GAP_SYMBOL || elem == '*' /* usage from HELMWebEditor */)
138
+ resWem = m.wem = new GapWebEditorMonomer(biotype);
125
139
  else if (
126
- (biotype === 'HELM_NUCLETIDE' && elem === 'N') ||
127
- (biotype === 'HELM_AA' && elem === 'X') ||
128
- (biotype === 'HELM_CHEM' && false) || // TODO: Ambiguous monomer for CHEM
140
+ (biotype === HelmTypes.NUCLEOTIDE && elem === 'N') ||
141
+ (biotype === HelmTypes.AA && elem === 'X') ||
142
+ (biotype === HelmTypes.CHEM && false) || // TODO: Ambiguous monomer for CHEM
129
143
  ambMonomerRe.test(elem) // e.g. (A,R,_)
130
144
  )
131
145
  resWem = m.wem = new AmbiguousWebEditorMonomer(biotype, elem);
132
146
  else if (!m.lib)
133
- resWem = m.wem = new MissingWebEditorMonomer(biotype, elem);
147
+ resWem = m.wem = new MissingWebEditorMonomer(biotype, elem, this.isEmpty);
134
148
 
135
149
  if (!resWem)
136
150
  resWem = m.wem = LibraryWebEditorMonomer.fromMonomer(biotype, m, this);
137
151
  }
138
152
 
139
- return resWem!;
153
+ return resWem;
154
+ }
155
+
156
+ getTooltip(biotype: HelmType, monomerSymbol: string): HTMLElement {
157
+ const polymerType = helmTypeToPolymerType(biotype);
158
+ const res = ui.div([], {classes: 'ui-form ui-tooltip'});
159
+ const monomer = this.getMonomer(polymerType, monomerSymbol);
160
+ if (monomer) {
161
+ // Symbol & Name
162
+ const symbol = monomer[REQ.SYMBOL];
163
+ const _name = monomer[REQ.NAME];
164
+ const wem = this.getWebEditorMonomer(biotype, monomerSymbol)!;
165
+
166
+ const htmlColor = wem.backgroundcolor;
167
+ res.append(ui.divH([
168
+ ui.div([symbol], {
169
+ style: {
170
+ /* fontWeight: 'bolder', */ textWrap: 'nowrap', marginLeft: '4px', marginRight: '4px',
171
+ color: wem.textcolor, backgroundColor: wem.backgroundcolor, borderColor: wem.linecolor,
172
+ borderWidth: '1px', borderStyle: 'solid', borderRadius: '2px', padding: '3px',
173
+ minWidth: '24px', textAlign: 'center',
174
+ }
175
+ }),
176
+ ui.div([monomer.name], {style: {padding: '4px'}}),
177
+ ], {style: {display: 'flex', flexDirection: 'row', justifyContent: 'left'}}));
178
+
179
+ // Structure
180
+ const chemOptions = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
181
+ let structureEl: HTMLElement;
182
+ if (monomer.molfile)
183
+ structureEl = grok.chem.svgMol(monomer.molfile, undefined, undefined, chemOptions);
184
+ else if (monomer.smiles) {
185
+ structureEl = ui.divV([
186
+ grok.chem.svgMol(monomer.smiles, undefined, undefined, chemOptions),
187
+ ui.divText('from smiles', {style: {fontSize: 'smaller'}}),
188
+ ]);
189
+ } else {
190
+ // Unable to get monomer's structure
191
+ structureEl = ui.divText('No structure', {style: {margin: '6px'}});
192
+ }
193
+ res.append(ui.div(structureEl,
194
+ {style: {display: 'flex', flexDirection: 'row', justifyContent: 'center', margin: '6px'}}));
195
+
196
+ // Source
197
+ if (monomer.symbol != GAP_SYMBOL)
198
+ res.append(ui.divText(monomer.lib?.source ?? 'Missed in libraries'));
199
+ } else {
200
+ res.append(ui.divV([
201
+ ui.divText(`Monomer '${monomerSymbol}' of type '${polymerType}' not found.`),
202
+ ui.divText('Open the Context Panel, then expand Manage Libraries'),
203
+ ]));
204
+ }
205
+ return res;
206
+ }
207
+
208
+ getMonomerTextColor(biotype: HelmType, symbol: string): string {
209
+ const colors: IMonomerColors = this.getMonomerColors(biotype, symbol);
210
+ const htmlToA = (html: string): number[] => {
211
+ const rgbM = /rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/.exec(html);
212
+ if (rgbM)
213
+ return [parseInt(rgbM[1]), parseInt(rgbM[2]), parseInt(rgbM[3])];
214
+
215
+ const n = DG.Color.fromHtml(colors.textcolor!);
216
+ return [DG.Color.r(n), DG.Color.g(n), DG.Color.b(n)];
217
+ };
218
+
219
+ const textColorA = htmlToA(colors.textcolor!);
220
+ const textColorAdjV = new Vector([...textColorA.map((v) => v + 1)]);
221
+ const cosTextToWhite = vectorDotProduct(textColorAdjV, whiteColorV) /
222
+ (vectorLength(textColorAdjV) * vectorLength(whiteColorV));
223
+
224
+ const backColorA = htmlToA(colors.backgroundcolor!);
225
+ const backColorAdjV = new Vector([...backColorA.map((v) => v + 0.01)]);
226
+ const cosBackToWhite = vectorDotProduct(backColorAdjV, whiteColorV) /
227
+ (vectorLength(backColorAdjV) * vectorLength(whiteColorV));
228
+
229
+ let resColorA;
230
+ if (cosBackToWhite < cosTextToWhite)
231
+ resColorA = backColorA;
232
+ else
233
+ resColorA = textColorA;
234
+
235
+ let resColorV = new Vector(resColorA);
236
+ const resColorLen = vectorLength(resColorV);
237
+ if (resColorLen > maxTextColorVLen) resColorV = vectorAdd(blackColorV, resColorV, maxTextColorVLen / resColorLen);
238
+ return `rgb(${resColorV[0]}, ${resColorV[1]}, ${resColorV[2]})`;
239
+ }
240
+
241
+ getMonomerColors(biotype: HelmType, symbol: string): IMonomerColors {
242
+ const currentMonomerSchema = 'default';
243
+ let monomerSchema: string = currentMonomerSchema;
244
+
245
+ const polymerType = helmTypeToPolymerType(biotype);
246
+ const monomer = this.getMonomer(polymerType, symbol)!;
247
+ let res: any;
248
+ if (monomer) {
249
+ if (monomer.meta && monomer.meta.colors) {
250
+ const monomerColors: { [colorSchemaName: string]: any } = monomer.meta.colors;
251
+ if (!(currentMonomerSchema in monomerColors)) monomerSchema = 'default';
252
+ res = monomerColors[monomerSchema];
253
+ }
254
+
255
+ if (!res) {
256
+ const biotypeColors: { [symbol: string]: string } | undefined = naturalMonomerColors[biotype];
257
+ const nColor: string = biotypeColors?.[monomer.symbol];
258
+ if (nColor) {
259
+ const nTextColor = DG.Color.toHtml(DG.Color.getContrastColor(DG.Color.fromHtml(nColor)));
260
+ res = {textColor: nTextColor, lineColor: '#202020', backgroundColor: nColor};
261
+ }
262
+ }
263
+
264
+ const naSymbol: string | undefined = monomer[OPT.NATURAL_ANALOG];
265
+ if (!res && naSymbol) {
266
+ return this.getMonomerColors(biotype, naSymbol);
267
+ }
268
+ }
269
+
270
+ if (!res)
271
+ res = {textColor: "#202020", lineColor: "#202020", backgroundColor: "#A0A0A0"};
272
+
273
+ return {
274
+ textcolor: res.text ?? res.textColor,
275
+ linecolor: res.line ?? res.lineColor,
276
+ backgroundcolor: res.background ?? res.backgroundColor
277
+ } as IMonomerColors;
140
278
  }
141
279
 
142
280
  getRS(smiles: string): { [r: 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
  }