@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.
- package/CHANGELOG.md +25 -0
- package/dist/284.js +1 -1
- package/dist/284.js.map +1 -1
- package/dist/package-test.js +3 -3
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +3 -3
- package/dist/package.js.map +1 -1
- package/files/monomer-libraries/sample-lib-Aca-colored.json +2 -2
- package/package.json +8 -8
- package/src/analysis/sequence-activity-cliffs.ts +10 -5
- package/src/analysis/sequence-similarity-viewer.ts +1 -1
- package/src/package.ts +1 -1
- package/src/tests/WebLogo-positions-test.ts +1 -1
- package/src/tests/splitters-test.ts +23 -6
- package/src/utils/cell-renderer-custom.ts +10 -2
- package/src/utils/cell-renderer.ts +102 -128
- package/src/utils/monomer-cell-renderer-base.ts +30 -0
- package/src/utils/monomer-cell-renderer.ts +73 -54
- package/src/utils/monomer-lib/lib-manager.ts +1 -1
- package/src/utils/monomer-lib/monomer-colors.ts +33 -30
- package/src/utils/monomer-lib/monomer-lib-base.ts +151 -13
- package/src/utils/monomer-lib/monomer-lib.ts +7 -50
- package/src/utils/monomer-lib/monomer-manager/monomer-manager.ts +94 -5
- package/src/utils/monomer-lib/web-editor-monomer-dummy.ts +11 -4
- package/src/utils/monomer-lib/web-editor-monomer-of-library.ts +5 -37
- package/src/viewers/web-logo-viewer.ts +40 -27
- package/src/widgets/composition-analysis-widget.ts +12 -17
|
@@ -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 {
|
|
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 {
|
|
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 {
|
|
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
|
|
21
|
-
|
|
20
|
+
export class MonomerCellRendererBack extends CellRendererWithMonomerLibBackBase {
|
|
21
|
+
constructor(gridCol: DG.GridColumn | null, tableCol: DG.Column) {
|
|
22
|
+
super(gridCol, tableCol);
|
|
23
|
+
}
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
61
|
+
override onMouseMove(gridCell: GridCell, e: MouseEvent) {
|
|
29
62
|
if (
|
|
30
|
-
gridCell.grid.dart != this.gridCol
|
|
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
|
-
|
|
41
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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.
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
|
137
|
+
if (elem === GAP_SYMBOL || elem == '*' /* usage from HELMWebEditor */)
|
|
138
|
+
resWem = m.wem = new GapWebEditorMonomer(biotype);
|
|
125
139
|
else if (
|
|
126
|
-
(biotype ===
|
|
127
|
-
(biotype ===
|
|
128
|
-
(biotype ===
|
|
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.
|
|
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
|
|
190
|
-
|
|
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
|
}
|