@datagrok/bio 1.5.7 → 1.5.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.
Files changed (36) hide show
  1. package/css/helm.css +3 -0
  2. package/detectors.js +9 -10
  3. package/dist/package-test.js +1095 -416
  4. package/dist/package.js +898 -250
  5. package/files/samples/sample_FASTA.csv +66 -66
  6. package/helm/JSDraw/Pistoia.HELM-uncompressed.js +9694 -0
  7. package/helm/JSDraw/Pistoia.HELM.js +27 -0
  8. package/helm/JSDraw/ReadMe.txt +8 -0
  9. package/helm/JSDraw/Scilligence.JSDraw2.Lite-uncompressed.js +31126 -0
  10. package/helm/JSDraw/Scilligence.JSDraw2.Lite.js +12 -0
  11. package/helm/JSDraw/Scilligence.JSDraw2.Resources.js +762 -0
  12. package/helm/JSDraw/dojo.js +250 -0
  13. package/helm/JSDraw/test.html +21 -0
  14. package/package.json +8 -1
  15. package/src/monomer-library.ts +199 -0
  16. package/src/package-test.ts +2 -0
  17. package/src/package.ts +41 -13
  18. package/src/tests/convert-test.ts +143 -22
  19. package/src/tests/detectors-test.ts +97 -156
  20. package/src/tests/renderer-test.ts +36 -0
  21. package/src/tests/splitter-test.ts +22 -0
  22. package/src/tests/types.ts +7 -0
  23. package/src/utils/atomic-works.ts +218 -97
  24. package/src/utils/cell-renderer.ts +214 -0
  25. package/src/utils/chem-palette.ts +280 -0
  26. package/src/utils/convert.ts +25 -16
  27. package/src/utils/misc.ts +29 -0
  28. package/src/utils/multiple-sequence-alignment.ts +1 -1
  29. package/src/utils/notation-converter.ts +120 -84
  30. package/src/utils/sequence-activity-cliffs.ts +2 -2
  31. package/src/utils/types.ts +13 -0
  32. package/src/utils/utils.ts +35 -30
  33. package/test-Bio-34f75e5127b8-c4c5a3dc.html +259 -0
  34. package/files/sample_FASTA.csv +0 -66
  35. package/files/sample_FASTA_with_activities.csv +0 -66
  36. package/files/sample_MSA.csv +0 -541
@@ -1,22 +1,84 @@
1
1
  import * as OCL from 'openchemlib/full.js';
2
+ import * as grok from 'datagrok-api/grok';
3
+
4
+ export async function getMacroMol(monomers: any[][]): Promise<string[]> {
5
+ let result: string[] = [];
6
+ const moduleRdkit = await grok.functions.call('Chem:getRdKitModule');
7
+ for(let i = 0; i < monomers.length; i++) {
8
+ for (let j = 0; j < monomers[i].length; j++){
9
+ const mol = moduleRdkit.get_mol(monomers[i][j]['molfile']);
10
+ const a = mol.get_v3Kmolblock();
11
+ const indices = getIndices(monomers[i][j], a);
12
+ monomers[i][j]['indices'] = indices;
13
+
14
+ monomers[i][j]['molfile'] = await rotateBackboneV3000(a, indices);
15
+ mol?.delete();
16
+ }
17
+ result.push(linkV3000(monomers[i]));
18
+ }
2
19
 
3
- export function getMacroMol(molBlocks: string[]): string {
4
- for (let i = 0; i < molBlocks.length; i++)
5
- molBlocks[i] = rotateBackboneV3000(molBlocks[i]);
20
+ return result;
21
+ }
22
+
23
+ function getIndices(monomer: any, molV3000: string): {first: number, last: number,
24
+ remFirst: number, remLast: number,
25
+ remBondFirst: number, remBondLast: Number} {
26
+ const molfile = monomer["molfile"];
27
+ let indexStart = molfile.indexOf('M RGP', 0) + 8;
28
+ let indexEnd = molfile.indexOf('\n', indexStart);
29
+ const indicesData = molfile.substring(indexStart, indexEnd).replaceAll(' ', ' ').replaceAll(' ', ' ');
30
+ let parsedData = indicesData.split(' ')
31
+ const remFirst = parsedData[2] == '1' ? parseInt(parsedData[1]) : parseInt(parsedData[3]);
32
+ const remLast = parsedData[2] == '2' ? parseInt(parsedData[1]) : parseInt(parsedData[3]);
33
+
34
+ const numbers = extractAtomsBondsNumbersV3000(molV3000);
35
+ let indexBonds = molV3000.indexOf('M V30 BEGIN BOND'); // V3000 index for bonds
36
+ indexBonds = molV3000.indexOf('\n', indexBonds);
37
+ indexStart = indexBonds;
38
+ indexEnd = indexBonds;
39
+
40
+ let first = 0;
41
+ let last = 0;
42
+ let remBondFirst = 0;
43
+ let remBondLast = 0;
44
+
45
+ for (let j = 0; j < numbers.nbond; j++) {
46
+ if(first == 0 || last == 0){
47
+ indexStart = molV3000.indexOf('V30', indexStart) + 4;
48
+ indexEnd = molV3000.indexOf('\n', indexStart);
49
+ const bondData = molV3000.substring(indexStart, indexEnd).replaceAll(' ', ' ').replaceAll(' ', ' ');
50
+ parsedData = bondData.split(' ')
51
+
52
+ if(parseInt(parsedData[2]) == remFirst){
53
+ first = parseInt(parsedData[3]);
54
+ remBondFirst = parseInt(parsedData[0]);
55
+ }
56
+ else if(parseInt(parsedData[3]) == remFirst){
57
+ first = parseInt(parsedData[2]);
58
+ remBondFirst = parseInt(parsedData[0]);
59
+ }
60
+ else if(parseInt(parsedData[2]) == remLast){
61
+ last = parseInt(parsedData[3]);
62
+ remBondLast = parseInt(parsedData[0]);
63
+ }
64
+ else if(parseInt(parsedData[3]) == remLast){
65
+ last = parseInt(parsedData[2]);
66
+ remBondLast = parseInt(parsedData[0]);
67
+ }
68
+ }
69
+ }
6
70
 
7
- return linkV3000(molBlocks);
71
+ return {first, last, remFirst, remLast, remBondFirst, remBondLast};
8
72
  }
9
73
 
10
- function rotateBackboneV3000(molecule: string): string {
11
- let molBlock = OCL.Molecule.fromMolfile(molecule).toMolfileV3();
74
+ async function rotateBackboneV3000(molBlock: string, indices:any): Promise<string> {
12
75
  const coordinates = extractAtomDataV3000(molBlock);
13
76
  const natom = coordinates.atomIndex.length;
77
+ const first = indices['first'];
78
+ const last = indices['last'];
14
79
 
15
- const indexFivePrime = coordinates.atomIndex.indexOf(1);
16
- const indexThreePrime = coordinates.atomIndex.indexOf(natom);
17
-
18
- const xCenter = (coordinates.x[indexThreePrime] + coordinates.x[indexFivePrime])/2;
19
- const yCenter = (coordinates.y[indexThreePrime] + coordinates.y[indexFivePrime])/2;
80
+ const xCenter = (coordinates.x[last] + coordinates.x[first])/2;
81
+ const yCenter = (coordinates.y[last] + coordinates.y[first])/2;
20
82
 
21
83
  //place to center
22
84
  for (let i = 0; i < natom; i++) {
@@ -25,13 +87,16 @@ function rotateBackboneV3000(molecule: string): string {
25
87
  }
26
88
 
27
89
  let angle = 0;
28
- if (coordinates.x[indexFivePrime] == 0)
29
- angle = coordinates.y[indexFivePrime] > coordinates.y[indexThreePrime] ? Math.PI/2 : 3*Math.PI/2;
30
- else if (coordinates.y[indexFivePrime] == 0)
31
- angle = coordinates.x[indexFivePrime] > coordinates.x[indexThreePrime] ? Math.PI : 0;
90
+ if (coordinates.x[first] == 0)
91
+ angle = coordinates.y[first] > coordinates.y[last] ? Math.PI/2 : 3*Math.PI/2;
92
+ else if (coordinates.y[first] == 0)
93
+ angle = coordinates.x[first] > coordinates.x[last] ? Math.PI : 0;
32
94
  else {
33
- const derivative = coordinates.y[indexFivePrime]/coordinates.x[indexFivePrime];
34
- angle = derivative > 0 ? Math.PI - Math.atan(derivative) : Math.atan(derivative);
95
+ const derivative = coordinates.y[first]/coordinates.x[first];
96
+ if(coordinates.x[first] < coordinates.x[last])
97
+ angle = derivative > 0 ? Math.PI - Math.atan(derivative) : Math.atan(derivative);
98
+ else
99
+ angle = derivative > 0 ? Math.atan(derivative) : Math.PI - Math.atan(derivative);
35
100
  }
36
101
 
37
102
  const cos = Math.cos(angle);
@@ -44,7 +109,7 @@ function rotateBackboneV3000(molecule: string): string {
44
109
  }
45
110
 
46
111
  //place to right
47
- const xShift = coordinates.x[indexFivePrime];
112
+ const xShift = coordinates.x[first];
48
113
  for (let i = 0; i < natom; i++)
49
114
  coordinates.x[i] -= xShift;
50
115
 
@@ -103,7 +168,7 @@ function extractAtomDataV3000(molBlock: string) {
103
168
  return {atomIndex: indexes, atomType: types, x: x, y: y};
104
169
  }
105
170
 
106
- function linkV3000(molBlocks: string[]): string {
171
+ function linkV3000(monomers: any[]): string {
107
172
  let macroMolBlock = '\nDatagrok macromolecule handler\n\n';
108
173
  macroMolBlock += ' 0 0 0 0 0 0 999 V3000\n';
109
174
  macroMolBlock += 'M V30 BEGIN CTAB\n';
@@ -115,112 +180,168 @@ function linkV3000(molBlocks: string[]): string {
115
180
  let nbond = 0;
116
181
  let xShift = 0;
117
182
 
118
- for (let i = 0; i < molBlocks.length; i++) {
119
- molBlocks[i] = molBlocks[i].replaceAll('(-\nM V30 ', '(')
183
+ for (let i = 0; i < monomers.length; i++) {
184
+ let molfile = monomers[i]['molfile'];
185
+ const first = monomers[i]['indices']['first'];
186
+ const last = monomers[i]['indices']['last'];
187
+ const remFirst = monomers[i]['indices']['remFirst'];
188
+ const remLast = monomers[i]['indices']['remLast'];
189
+ const remBondFirst = monomers[i]['indices']['remBondFirst'];
190
+ const remBondLast = monomers[i]['indices']['remBondLast'];
191
+ molfile = molfile.replaceAll('(-\nM V30 ', '(')
120
192
  .replaceAll('-\nM V30 ', '').replaceAll(' )', ')');
121
- const numbers = extractAtomsBondsNumbersV3000(molBlocks[i]);
122
- const coordinates = extractAtomDataV3000(molBlocks[i]);
193
+ const numbers = extractAtomsBondsNumbersV3000(molfile);
194
+ const coordinates = extractAtomDataV3000(molfile);
123
195
 
124
- let indexAtoms = molBlocks[i].indexOf('M V30 BEGIN ATOM'); // V3000 index for atoms coordinates
125
- indexAtoms = molBlocks[i].indexOf('\n', indexAtoms);
196
+ let indexAtoms = molfile.indexOf('M V30 BEGIN ATOM'); // V3000 index for atoms coordinates
197
+ indexAtoms = molfile.indexOf('\n', indexAtoms);
126
198
  let index = indexAtoms;
127
199
  let indexEnd = indexAtoms;
200
+ const totalShift = xShift - coordinates.x[first - 1];
128
201
 
129
202
  for (let j = 0; j < numbers.natom; j++) {
130
- if (coordinates.atomIndex[j] != 1 || i == 0) {
203
+ if (coordinates.atomIndex[j] != remFirst && coordinates.atomIndex[j] != remLast) { //|| i == 0) {
131
204
  //rewrite atom number
132
- index = molBlocks[i].indexOf('V30', index) + 4;
133
- indexEnd = molBlocks[i].indexOf(' ', index);
134
- const atomNumber = parseInt(molBlocks[i].substring(index, indexEnd)) + natom;
135
- molBlocks[i] = molBlocks[i].slice(0, index) + atomNumber + molBlocks[i].slice(indexEnd);
205
+ index = molfile.indexOf('V30', index) + 4;
206
+ indexEnd = molfile.indexOf(' ', index);
207
+
208
+ let atomNumber = parseInt(molfile.substring(index, indexEnd))
209
+ atomNumber = (atomNumber > remFirst && atomNumber > remLast) ? atomNumber - 2 :
210
+ (atomNumber > remFirst || atomNumber > remLast) ? atomNumber - 1 : atomNumber;
211
+ atomNumber += natom;
212
+ molfile = molfile.slice(0, index) + atomNumber + molfile.slice(indexEnd);
136
213
 
137
214
  //rewrite coordinates
138
- index = molBlocks[i].indexOf(' ', index) + 1;
139
- index = molBlocks[i].indexOf(' ', index) + 1;
140
- indexEnd = molBlocks[i].indexOf(' ', index);
215
+ index = molfile.indexOf(' ', index) + 1;
216
+ index = molfile.indexOf(' ', index) + 1;
217
+ indexEnd = molfile.indexOf(' ', index);
141
218
 
142
- const totalShift = xShift - coordinates.x[0];
143
- let coordinate = Math.round(10000*(parseFloat(molBlocks[i].substring(index, indexEnd)) + totalShift))/10000;
144
- molBlocks[i] = molBlocks[i].slice(0, index) + coordinate + molBlocks[i].slice(indexEnd);
219
+ let coordinate = Math.round(10000*(parseFloat(molfile.substring(index, indexEnd)) + totalShift))/10000;
220
+ molfile = molfile.slice(0, index) + coordinate + molfile.slice(indexEnd);
145
221
 
146
- index = molBlocks[i].indexOf(' ', index) + 1;
147
- indexEnd = molBlocks[i].indexOf(' ', index);
148
- coordinate = Math.round(10000*(parseFloat(molBlocks[i].substring(index, indexEnd))))/10000;
149
- molBlocks[i] = molBlocks[i].slice(0, index) + coordinate + molBlocks[i].slice(indexEnd);
222
+ index = molfile.indexOf(' ', index) + 1;
223
+ indexEnd = molfile.indexOf(' ', index);
224
+ coordinate = Math.round(10000*(parseFloat(molfile.substring(index, indexEnd))))/10000;
225
+ molfile = molfile.slice(0, index) + coordinate + molfile.slice(indexEnd);
150
226
 
151
- index = molBlocks[i].indexOf('\n', index) + 1;
227
+ index = molfile.indexOf('\n', index) + 1;
152
228
  } else {
153
- index = molBlocks[i].indexOf('M V30', index) - 1;
154
- indexEnd = molBlocks[i].indexOf('\n', index + 1);
155
- molBlocks[i] = molBlocks[i].slice(0, index) + molBlocks[i].slice(indexEnd);
229
+ index = molfile.indexOf('M V30', index) - 1;
230
+ indexEnd = molfile.indexOf('\n', index + 1);
231
+ molfile = molfile.slice(0, index) + molfile.slice(indexEnd);
156
232
  }
157
233
  }
158
234
 
159
- const indexAtomsEnd = molBlocks[i].indexOf('M V30 END ATOM');
160
- atomBlock += molBlocks[i].substring(indexAtoms + 1, indexAtomsEnd);
235
+ const indexAtomsEnd = molfile.indexOf('M V30 END ATOM');
236
+ atomBlock += molfile.substring(indexAtoms + 1, indexAtomsEnd);
161
237
 
162
- let indexBonds = molBlocks[i].indexOf('M V30 BEGIN BOND'); // V3000 index for bonds
163
- indexBonds = molBlocks[i].indexOf('\n', indexBonds);
238
+ let indexBonds = molfile.indexOf('M V30 BEGIN BOND'); // V3000 index for bonds
239
+ indexBonds = molfile.indexOf('\n', indexBonds);
164
240
  index = indexBonds;
165
241
  indexEnd = indexBonds;
242
+ let bondNumber = 0;
166
243
 
167
244
  for (let j = 0; j < numbers.nbond; j++) {
168
245
  //rewrite bond number
169
- index = molBlocks[i].indexOf('V30', index) + 4;
170
- indexEnd = molBlocks[i].indexOf(' ', index);
171
- const bondNumber = parseInt(molBlocks[i].substring(index, indexEnd)) + nbond;
172
- molBlocks[i] = molBlocks[i].slice(0, index) + bondNumber + molBlocks[i].slice(indexEnd);
173
-
174
- //rewrite atom pair in bond
175
- index = molBlocks[i].indexOf(' ', index) + 1;
176
- index = molBlocks[i].indexOf(' ', index) + 1;
177
- indexEnd = molBlocks[i].indexOf(' ', index);
178
- let atomNumber = parseInt(molBlocks[i].substring(index, indexEnd)) + natom;
179
- molBlocks[i] = molBlocks[i].slice(0, index) + atomNumber + molBlocks[i].slice(indexEnd);
180
- index = molBlocks[i].indexOf(' ', index) + 1;
181
- indexEnd = Math.min(molBlocks[i].indexOf('\n', index), molBlocks[i].indexOf(' ', index));
182
- atomNumber = parseInt(molBlocks[i].substring(index, indexEnd)) + natom;
183
- molBlocks[i] = molBlocks[i].slice(0, index) + atomNumber + molBlocks[i].slice(indexEnd);
184
-
185
- index = molBlocks[i].indexOf('\n', index) + 1;
186
- }
246
+ index = molfile.indexOf('V30', index) + 4;
247
+ indexEnd = molfile.indexOf(' ', index);
248
+ bondNumber = parseInt(molfile.substring(index, indexEnd));
249
+
250
+ if(bondNumber == remBondFirst || bondNumber == remBondLast){
251
+ indexEnd = molfile.indexOf('\n', index) + 1;
252
+ index -=7;
253
+ molfile = molfile.slice(0, index) + molfile.slice(indexEnd);
254
+ continue
255
+ }
187
256
 
188
- const indexBondEnd = molBlocks[i].indexOf('M V30 END BOND');
189
- bondBlock += molBlocks[i].substring(indexBonds + 1, indexBondEnd);
257
+ bondNumber = (bondNumber > remBondFirst && bondNumber > remBondLast) ? bondNumber - 2 :
258
+ (bondNumber > remBondFirst || bondNumber > remBondLast) ? bondNumber - 1 : bondNumber;
259
+ bondNumber += nbond;
190
260
 
191
- let indexCollection = molBlocks[i].indexOf('M V30 MDLV30/STEABS ATOMS=('); // V3000 index for collections
261
+ molfile = molfile.slice(0, index) + bondNumber + molfile.slice(indexEnd);
192
262
 
193
- while (indexCollection != -1) {
194
- indexCollection += 28;
195
- const collectionEnd = molBlocks[i].indexOf(')', indexCollection);
196
- const collectionEntries = molBlocks[i].substring(indexCollection, collectionEnd).split(' ').slice(1);
197
- collectionEntries.forEach((e) => {
198
- collection.push(parseInt(e) + natom);
199
- });
200
- indexCollection = collectionEnd;
201
- indexCollection = molBlocks[i].indexOf('M V30 MDLV30/STEABS ATOMS=(', indexCollection);
263
+ //rewrite atom pair in bond
264
+ index = molfile.indexOf(' ', index) + 1;
265
+ index = molfile.indexOf(' ', index) + 1;
266
+ indexEnd = molfile.indexOf(' ', index);
267
+ let atomNumber = parseInt(molfile.substring(index, indexEnd))
268
+ atomNumber = (atomNumber > remFirst && atomNumber > remLast) ? atomNumber - 2 :
269
+ (atomNumber > remFirst || atomNumber > remLast) ? atomNumber - 1 : atomNumber;
270
+ atomNumber += natom;
271
+ molfile = molfile.slice(0, index) + atomNumber + molfile.slice(indexEnd);
272
+ index = molfile.indexOf(' ', index) + 1;
273
+ indexEnd = Math.min(molfile.indexOf('\n', index), molfile.indexOf(' ', index));
274
+ atomNumber = parseInt(molfile.substring(index, indexEnd))
275
+ atomNumber = (atomNumber > remFirst && atomNumber > remLast) ? atomNumber - 2 :
276
+ (atomNumber > remFirst || atomNumber > remLast) ? atomNumber - 1 : atomNumber;
277
+ atomNumber += natom;
278
+ molfile = molfile.slice(0, index) + atomNumber + molfile.slice(indexEnd);
279
+
280
+ index = molfile.indexOf('\n', index) + 1;
202
281
  }
203
282
 
204
- natom += numbers.natom - 1;
205
- nbond += numbers.nbond;
206
- xShift += coordinates.x[numbers.natom - 1] - coordinates.x[0];
207
- }
208
-
209
- const entries = 4;
210
- const collNumber = Math.ceil(collection.length / entries);
211
- collectionBlock += 'M V30 MDLV30/STEABS ATOMS=(' + collection.length + ' -\n';
212
- for (let i = 0; i < collNumber; i++) {
213
- collectionBlock += 'M V30 ';
214
- const entriesCurrent = i + 1 == collNumber ? collection.length - (collNumber - 1)*entries : entries;
215
- for (let j = 0; j < entriesCurrent; j++) {
216
- collectionBlock += (j + 1 == entriesCurrent) ?
217
- (i == collNumber - 1 ? collection[entries*i + j] + ')\n' : collection[entries*i + j] + ' -\n') :
218
- collection[entries*i + j] + ' ';
283
+ const indexBondEnd = molfile.indexOf('M V30 END BOND');
284
+ bondBlock += molfile.substring(indexBonds + 1, indexBondEnd);
285
+ //let indexCollection = molfile.indexOf('M V30 MDLV30/STEABS ATOMS=('); // V3000 index for collections
286
+
287
+ // while (indexCollection != -1) {
288
+ // indexCollection += 28;
289
+ // const collectionEnd = molfile.indexOf(')', indexCollection);
290
+ // const collectionEntries = molfile.substring(indexCollection, collectionEnd).split(' ').slice(1);
291
+ // collectionEntries.forEach((e: string) => {
292
+ // collection.push(parseInt(e) + natom);
293
+ // });
294
+ // indexCollection = collectionEnd;
295
+ // indexCollection = molfile.indexOf('M V30 MDLV30/STEABS ATOMS=(', indexCollection);
296
+ // }
297
+
298
+ natom += numbers.natom - 2;
299
+ nbond += numbers.nbond - 2;
300
+ xShift += coordinates.x[last] - coordinates.x[first] + 1;
301
+
302
+ if(i == monomers.length -1){
303
+ natom++;
304
+ const shift = xShift + 0.2;
305
+ atomBlock += 'M V30 ' + natom + ' O ' + shift + ' 0 0.000000 0\n';
306
+ }
307
+ nbond++;
308
+ if(i == monomers.length -1){
309
+ const rightTerminal = (last > remFirst && last > remLast) ? last + natom - (numbers.natom - 2) - 3:
310
+ (last > remFirst || last > remLast) ? last + natom - (numbers.natom - 2) - 2 :
311
+ last + natom - (numbers.natom - 2) - 1;
312
+ bondBlock += 'M V30 ' + nbond + ' 1 ' + rightTerminal + ' ' + natom + '\n';
313
+ } else{
314
+ const rightTerminal = (last > remFirst && last > remLast) ? last + natom - (numbers.natom - 2) - 2:
315
+ (last > remFirst || last > remLast) ? last + natom - (numbers.natom - 2) - 1 :
316
+ last + natom - (numbers.natom - 2);
317
+
318
+ const next = monomers[i + 1]['indices'];
319
+ const nextFirst = next['first'];
320
+ const nextRemFirst = next['remFirst'];
321
+ const nextRemLast = next['remLast'];
322
+
323
+ const leftTerminal = (nextFirst > nextRemFirst && nextFirst > nextRemLast) ? nextFirst + natom - 2 :
324
+ (nextFirst > nextRemFirst || nextFirst > nextRemLast) ? nextFirst + natom - 1 :
325
+ nextFirst + natom;
326
+
327
+ bondBlock += 'M V30 ' + nbond + ' 1 ' + rightTerminal + ' ' + leftTerminal + '\n';
219
328
  }
220
329
  }
221
330
 
331
+ // const entries = 4;
332
+ // const collNumber = Math.ceil(collection.length / entries);
333
+ // collectionBlock += 'M V30 MDLV30/STEABS ATOMS=(' + collection.length + ' -\n';
334
+ // for (let i = 0; i < collNumber; i++) {
335
+ // collectionBlock += 'M V30 ';
336
+ // const entriesCurrent = i + 1 == collNumber ? collection.length - (collNumber - 1)*entries : entries;
337
+ // for (let j = 0; j < entriesCurrent; j++) {
338
+ // collectionBlock += (j + 1 == entriesCurrent) ?
339
+ // (i == collNumber - 1 ? collection[entries*i + j] + ')\n' : collection[entries*i + j] + ' -\n') :
340
+ // collection[entries*i + j] + ' ';
341
+ // }
342
+ // }
343
+
222
344
  //generate file
223
- natom++;
224
345
  macroMolBlock += 'M V30 COUNTS ' + natom + ' ' + nbond + ' 0 0 0\n';
225
346
  macroMolBlock += 'M V30 BEGIN ATOM\n';
226
347
  macroMolBlock += atomBlock;
@@ -228,9 +349,9 @@ function linkV3000(molBlocks: string[]): string {
228
349
  macroMolBlock += 'M V30 BEGIN BOND\n';
229
350
  macroMolBlock += bondBlock;
230
351
  macroMolBlock += 'M V30 END BOND\n';
231
- macroMolBlock += 'M V30 BEGIN COLLECTION\n';
232
- macroMolBlock += collectionBlock;
233
- macroMolBlock += 'M V30 END COLLECTION\n';
352
+ //macroMolBlock += 'M V30 BEGIN COLLECTION\n';
353
+ //macroMolBlock += collectionBlock;
354
+ //macroMolBlock += 'M V30 END COLLECTION\n';
234
355
  macroMolBlock += 'M V30 END CTAB\n';
235
356
  macroMolBlock += 'M END\n';
236
357
 
@@ -0,0 +1,214 @@
1
+ import * as C from "./constants";
2
+ import {getSeparator} from "./misc";
3
+ import {ChemPalette} from "./chem-palette";
4
+ import * as DG from 'datagrok-api/dg';
5
+ import {AminoacidsPalettes} from "@datagrok-libraries/bio/src/aminoacids";
6
+ import {NucleotidesPalettes} from "@datagrok-libraries/bio/src/nucleotides";
7
+ import {UnknownSeqPalettes} from "@datagrok-libraries/bio/src/unknown";
8
+ import {SplitterFunc, WebLogo} from "@datagrok-libraries/bio/src/viewers/web-logo";
9
+ import {SeqPalette} from "@datagrok-libraries/bio/src/seq-palettes";
10
+ import * as ui from 'datagrok-api/ui';
11
+
12
+ const lru = new DG.LruCache<any, any>();
13
+
14
+ function getPalleteByType(paletteType: string): SeqPalette {
15
+ switch (paletteType) {
16
+ case 'PT':
17
+ return AminoacidsPalettes.GrokGroups;
18
+ case 'NT':
19
+ return NucleotidesPalettes.Chromatogram
20
+ // other
21
+ default:
22
+ return UnknownSeqPalettes.Color;
23
+ }
24
+ }
25
+
26
+ export function processSequence(subParts: string[]): [string[], boolean] {
27
+ const simplified = !subParts.some((amino, index) =>
28
+ amino.length > 1 &&
29
+ index != 0 &&
30
+ index != subParts.length - 1);
31
+
32
+ const text: string[] = [];
33
+ const gap = simplified ? '' : ' ';
34
+ subParts.forEach((amino: string, index) => {
35
+ if (index < subParts.length)
36
+ amino += `${amino ? '' : '-'}${gap}`;
37
+
38
+ text.push(amino);
39
+ });
40
+ return [text, simplified];
41
+ }
42
+ /**
43
+ * A function that prints a string aligned to left or centered.
44
+ *
45
+ * @param {number} x x coordinate.
46
+ * @param {number} y y coordinate.
47
+ * @param {number} w Width.
48
+ * @param {number} h Height.
49
+ * @param {CanvasRenderingContext2D} g Canvas rendering context.
50
+ * @param {string} s String to print.
51
+ * @param {string} [color=ChemPalette.undefinedColor] String color.
52
+ * @param {number} [pivot=0] Pirvot.
53
+ * @param {boolean} [left=false] Is left aligned.
54
+ * @param {boolean} [hideMod=false] Hide amino acid redidue modifications.
55
+ * @param {number} [transparencyRate=0.0] Transparency rate where 1.0 is fully transparent
56
+ * @return {number} x coordinate to start printing at.
57
+ */
58
+ function printLeftOrCentered(
59
+ x: number, y: number, w: number, h: number,
60
+ g: CanvasRenderingContext2D, s: string, color = ChemPalette.undefinedColor,
61
+ pivot: number = 0, left = false, hideMod = false, transparencyRate: number = 1.0,
62
+ separator: string = ''): number {
63
+ g.textAlign = 'start';
64
+ let colorPart = s.substring(0);
65
+ let grayPart = separator;
66
+ const textSize = g.measureText(colorPart + grayPart);
67
+ const indent = 5;
68
+
69
+ const colorTextSize = g.measureText(colorPart);
70
+ const dy = (textSize.fontBoundingBoxAscent + textSize.fontBoundingBoxDescent) / 2;
71
+
72
+ function draw(dx1: number, dx2: number): void {
73
+ g.fillStyle = color;
74
+ g.globalAlpha = transparencyRate;
75
+ g.fillText(colorPart, x + dx1, y + dy);
76
+ g.fillStyle = '#808080';
77
+ g.fillText(grayPart, x + dx2, y + dy);
78
+ }
79
+
80
+
81
+ if (left || textSize.width > w) {
82
+ draw(indent, indent + colorTextSize.width);
83
+ return x + colorTextSize.width + g.measureText(grayPart).width;
84
+ } else {
85
+ const dx = (w - textSize.width) / 2;
86
+ draw(dx, dx + colorTextSize.width);
87
+ return x + dx + colorTextSize.width;
88
+ }
89
+ }
90
+ function renderSequense(
91
+ g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, gridCell: DG.GridCell,
92
+ cellStyle: DG.GridCellStyle,
93
+ ): void {
94
+ const grid = gridCell.grid;
95
+ const cell = gridCell.cell;
96
+ const [type, subtype, paletteType] = gridCell.cell.column.getTag(DG.TAGS.UNITS).split(":");
97
+ w = grid ? Math.min(grid.canvas.width - x, w) : g.canvas.width - x;
98
+ g.save();
99
+ g.beginPath();
100
+ g.rect(x, y, w, h);
101
+ g.clip();
102
+ g.font = '12px monospace';
103
+ g.textBaseline = 'top';
104
+ const s: string = cell.value ?? '';
105
+
106
+ //TODO: can this be replaced/merged with splitSequence?
107
+ const units = gridCell.cell.column.getTag(DG.TAGS.UNITS);
108
+
109
+ const palette = getPalleteByType(paletteType);
110
+
111
+ const separator = gridCell.cell.column.getTag('separator') ?? '';
112
+ const splitterFunc: SplitterFunc = WebLogo.getSplitter(units, gridCell.cell.column.getTag('separator') );// splitter,
113
+
114
+ const subParts:string[] = splitterFunc(cell.value);
115
+
116
+ const textSize = g.measureText(subParts.join(''));
117
+ let x1 = Math.max(x, x + (w - textSize.width) / 2);
118
+
119
+ subParts.forEach((amino, index) => {
120
+ let [color, outerAmino,, pivot] = ChemPalette.getColorAAPivot(amino);
121
+ color = palette.get(amino);
122
+ g.fillStyle = ChemPalette.undefinedColor;
123
+ x1 = printLeftOrCentered(x1, y, w, h, g, amino, color, pivot, true, false, 1.0, separator);
124
+ });
125
+
126
+ g.restore();
127
+ }
128
+
129
+ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
130
+ constructor() {
131
+ super();
132
+ }
133
+
134
+ get name(): string {return 'macromoleculeSequence';}
135
+
136
+ get cellType(): string {return C.SEM_TYPES.Macro_Molecule;}
137
+
138
+ get defaultHeight(): number {return 30;}
139
+
140
+ get defaultWidth(): number {return 230;}
141
+
142
+ /**
143
+ * Cell renderer function.
144
+ *
145
+ * @param {CanvasRenderingContext2D} g Canvas rendering context.
146
+ * @param {number} x x coordinate on the canvas.
147
+ * @param {number} y y coordinate on the canvas.
148
+ * @param {number} w width of the cell.
149
+ * @param {number} h height of the cell.
150
+ * @param {DG.GridCell} gridCell Grid cell.
151
+ * @param {DG.GridCellStyle} cellStyle Cell style.
152
+ * @memberof AlignedSequenceCellRenderer
153
+ */
154
+ render(
155
+ g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, gridCell: DG.GridCell,
156
+ cellStyle: DG.GridCellStyle,
157
+ ): void {
158
+ const grid = gridCell.grid;
159
+ const cell = gridCell.cell;
160
+ const tag = gridCell.cell.column.getTag(DG.TAGS.UNITS);
161
+ if (tag === 'HELM') {
162
+ let host = ui.div([], { style: { width: `${w}px`, height: `${h}px`}});
163
+ host.setAttribute('dataformat', 'helm');
164
+ host.setAttribute('data', gridCell.cell.value);
165
+
166
+ //@ts-ignore
167
+ var canvas = new JSDraw2.Editor(host, { width: w, height: h, skin: "w8", viewonly: true });
168
+ var formula = canvas.getFormula(true);
169
+ if (!formula) {
170
+ gridCell.element = ui.divText(gridCell.cell.value, {style: {color: 'red'}});
171
+ } else {
172
+ gridCell.element = host;
173
+ var molWeight = Math.round(canvas.getMolWeight() * 100) / 100;
174
+ var coef = Math.round(canvas.getExtinctionCoefficient(true) * 100) / 100;
175
+ var molfile = canvas.getMolfile();
176
+ var result = formula + ', ' + molWeight + ', ' + coef + ', ' + molfile;
177
+ lru.set(gridCell.cell.value, result);
178
+ }
179
+ } else {
180
+ const [type, subtype, paletteType] = gridCell.cell.column.getTag(DG.TAGS.UNITS).split(":");
181
+ w = grid ? Math.min(grid.canvas.width - x, w) : g.canvas.width - x;
182
+ g.save();
183
+ g.beginPath();
184
+ g.rect(x, y, w, h);
185
+ g.clip();
186
+ g.font = '12px monospace';
187
+ g.textBaseline = 'top';
188
+ const s: string = cell.value ?? '';
189
+
190
+ //TODO: can this be replaced/merged with splitSequence?
191
+ const units = gridCell.cell.column.getTag(DG.TAGS.UNITS);
192
+
193
+ const palette = getPalleteByType(paletteType);
194
+
195
+ const separator = gridCell.cell.column.getTag('separator') ?? '';
196
+ const splitterFunc: SplitterFunc = WebLogo.getSplitter(units, gridCell.cell.column.getTag('separator') );// splitter,
197
+
198
+ const subParts:string[] = splitterFunc(cell.value);
199
+ console.log(subParts);
200
+
201
+ const textSize = g.measureText(subParts.join(''));
202
+ let x1 = Math.max(x, x + (w - textSize.width) / 2);
203
+
204
+ subParts.forEach((amino, index) => {
205
+ let [color, outerAmino,, pivot] = ChemPalette.getColorAAPivot(amino);
206
+ color = palette.get(amino);
207
+ g.fillStyle = ChemPalette.undefinedColor;
208
+ x1 = printLeftOrCentered(x1, y, w, h, g, amino, color, pivot, true, false, 1.0, separator);
209
+ });
210
+
211
+ g.restore();
212
+ }
213
+ }
214
+ }