@datagrok/bio 2.12.11 → 2.12.13

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 (55) hide show
  1. package/.eslintrc.json +4 -1
  2. package/CHANGELOG.md +10 -0
  3. package/dist/246.js +2 -0
  4. package/dist/246.js.map +1 -0
  5. package/dist/42.js +1 -1
  6. package/dist/42.js.map +1 -1
  7. package/dist/545.js +3 -0
  8. package/dist/545.js.map +1 -0
  9. package/dist/590.js.map +1 -1
  10. package/dist/package-test.js +5 -5
  11. package/dist/package-test.js.LICENSE.txt +0 -8
  12. package/dist/package-test.js.map +1 -1
  13. package/dist/package.js +5 -5
  14. package/dist/package.js.LICENSE.txt +0 -8
  15. package/dist/package.js.map +1 -1
  16. package/package.json +7 -6
  17. package/src/package.ts +2 -2
  18. package/src/tests/renderers-test.ts +47 -1
  19. package/src/utils/cell-renderer.ts +28 -3
  20. package/src/utils/helm-to-molfile/converter/connection-list.ts +40 -0
  21. package/src/utils/helm-to-molfile/converter/const.ts +4 -0
  22. package/src/utils/helm-to-molfile/converter/converter.ts +124 -0
  23. package/src/utils/helm-to-molfile/converter/helm.ts +112 -0
  24. package/src/utils/helm-to-molfile/converter/index.ts +1 -0
  25. package/src/utils/helm-to-molfile/converter/mol-atoms-v2k.ts +24 -0
  26. package/src/utils/helm-to-molfile/converter/mol-atoms-v3k.ts +38 -0
  27. package/src/utils/helm-to-molfile/converter/mol-atoms.ts +44 -0
  28. package/src/utils/helm-to-molfile/converter/mol-bonds-v2k.ts +26 -0
  29. package/src/utils/helm-to-molfile/converter/mol-bonds-v3k.ts +30 -0
  30. package/src/utils/helm-to-molfile/converter/mol-bonds.ts +56 -0
  31. package/src/utils/helm-to-molfile/converter/mol-wrapper-factory.ts +16 -0
  32. package/src/utils/helm-to-molfile/converter/mol-wrapper-old.ts +100 -0
  33. package/src/utils/helm-to-molfile/converter/mol-wrapper-v2k.ts +21 -0
  34. package/src/utils/helm-to-molfile/converter/mol-wrapper-v3k.ts +21 -0
  35. package/src/utils/helm-to-molfile/converter/mol-wrapper.ts +79 -0
  36. package/src/utils/helm-to-molfile/converter/monomer-wrapper.ts +103 -0
  37. package/src/utils/helm-to-molfile/converter/polymer.ts +99 -0
  38. package/src/utils/helm-to-molfile/converter/position-handler.ts +23 -0
  39. package/src/utils/helm-to-molfile/converter/r-group-handler.ts +122 -0
  40. package/src/utils/helm-to-molfile/converter/simple-polymer.ts +89 -0
  41. package/src/utils/helm-to-molfile/converter/types.ts +12 -0
  42. package/src/utils/helm-to-molfile/utils.ts +32 -0
  43. package/src/utils/poly-tool/const.ts +0 -4
  44. package/src/utils/poly-tool/transformation.ts +126 -62
  45. package/src/utils/sequence-to-mol.ts +1 -1
  46. package/webpack.config.js +4 -3
  47. package/dist/709.js +0 -2
  48. package/dist/709.js.map +0 -1
  49. package/dist/777.js +0 -3
  50. package/dist/777.js.map +0 -1
  51. package/link-bio +0 -7
  52. package/setup +0 -52
  53. package/src/utils/atomic-works.ts +0 -367
  54. package/src/utils/helm-to-molfile.ts +0 -959
  55. /package/dist/{777.js.LICENSE.txt → 545.js.LICENSE.txt} +0 -0
@@ -0,0 +1,56 @@
1
+ import {PositionInBonds} from './types';
2
+
3
+ export abstract class MolfileBonds {
4
+ protected bondedAtomPairs: number[][] = [];
5
+ protected rawBondLines: string[] = [];
6
+
7
+ /** Get bond lines with new values for bonded atoms */
8
+ abstract getBondLines(): string[];
9
+
10
+ get bondedAtoms(): number[][] {
11
+ return this.bondedAtomPairs;
12
+ }
13
+
14
+ deleteBondLines(indices: number[]): void {
15
+ this.rawBondLines = this.rawBondLines.filter((_, idx) => !indices.includes(idx));
16
+ this.bondedAtomPairs = this.bondedAtomPairs.filter((_, idx) => !indices.includes(idx));
17
+ }
18
+
19
+ /** Atom id starts from 1 */
20
+ getPositionsInBonds(atomId: number): PositionInBonds[] {
21
+ const positions: PositionInBonds[] = [];
22
+ this.bondedAtomPairs.forEach((bondedPair, bondLineIdx) => {
23
+ bondedPair.forEach((atom, nodeIdx) => {
24
+ if (atom === atomId)
25
+ positions.push({bondLineIdx, nodeIdx});
26
+ });
27
+ });
28
+ return positions;
29
+ }
30
+
31
+ replacePositionsInBondsByDummy(positions: PositionInBonds[], dummy?: number): void {
32
+ if (dummy === undefined)
33
+ dummy = -1;
34
+ positions.forEach((position) => {
35
+ const {bondLineIdx, nodeIdx} = position;
36
+ this.bondedAtomPairs[bondLineIdx][nodeIdx] = dummy!;
37
+ });
38
+ }
39
+
40
+ removeAtomIdFromBonds(atomId: number): void {
41
+ this.bondedAtomPairs = this.bondedAtomPairs.map((bondedPair) => {
42
+ return bondedPair.map((id) => {
43
+ if (id > atomId)
44
+ return id - 1;
45
+ return id;
46
+ });
47
+ });
48
+ }
49
+
50
+ shift(shift: number): void {
51
+ this.bondedAtomPairs = this.bondedAtomPairs.map((bondedPair) => {
52
+ return bondedPair.map((id) => id + shift);
53
+ });
54
+ }
55
+ }
56
+
@@ -0,0 +1,16 @@
1
+ import {MolfileHandler} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler';
2
+ import {MolfileWrapper} from './mol-wrapper';
3
+ import {MolfileV2KWrapper} from './mol-wrapper-v2k';
4
+ import {MolfileV3KWrapper} from './mol-wrapper-v3k';
5
+
6
+ export class MolfileWrapperFactory {
7
+ static getInstance(molfile: string, monomerSymbol: string): MolfileWrapper {
8
+ if (MolfileHandler.isMolfileV2K(molfile))
9
+ return new MolfileV2KWrapper(molfile, monomerSymbol) as MolfileWrapper;
10
+ else if (MolfileHandler.isMolfileV3K(molfile))
11
+ return new MolfileV3KWrapper(molfile, monomerSymbol) as MolfileWrapper;
12
+ else
13
+ throw new Error('Unsupported molfile version');
14
+ }
15
+ }
16
+
@@ -0,0 +1,100 @@
1
+ // import {MolfileAtoms} from './mol-atoms';
2
+ // import {MolfileBonds} from './mol-bonds';
3
+ // import {RGroupHandler} from './r-group-handler';
4
+
5
+ // export class MolfileWrapper {
6
+ // constructor(molfileV2K: string, private monomerSymbol: string) {
7
+ // const lines = molfileV2K.split('\n');
8
+
9
+ // // TODO: port to consts
10
+ // const atomCountIdx = {begin: 0, end: 3};
11
+ // const bondCountIdx = {begin: 3, end: 6};
12
+ // const countsLineIdx = 3;
13
+ // const atomBlockIdx = 4;
14
+
15
+ // const atomCount = parseInt(lines[countsLineIdx].substring(atomCountIdx.begin, atomCountIdx.end));
16
+ // const bondCount = parseInt(lines[countsLineIdx].substring(bondCountIdx.begin, bondCountIdx.end));
17
+
18
+ // const atomLines = lines.slice(atomBlockIdx, atomBlockIdx + atomCount);
19
+ // this.atoms = new MolfileAtoms(atomLines);
20
+
21
+ // const bondLines = lines.slice(atomBlockIdx + atomCount, atomBlockIdx + atomCount + bondCount);
22
+ // this.bonds = new MolfileBonds(bondLines);
23
+
24
+ // this.rGroups = new RGroupHandler(lines, this.atoms, this.bonds);
25
+
26
+ // this.shiftMonomerToDefaultPosition();
27
+ // }
28
+
29
+ // private atoms: MolfileAtoms;
30
+ // private bonds: MolfileBonds;
31
+ // private rGroups: RGroupHandler;
32
+
33
+ // deleteBondLineWithSpecifiedRGroup(rGroupId: number): void {
34
+ // this.rGroups.deleteBondLineWithSpecifiedRGroup(rGroupId);
35
+ // }
36
+
37
+ // shiftCoordinates(shift: {x: number, y: number}): void {
38
+ // this.atoms.shift(shift);
39
+ // }
40
+
41
+ // rotateCoordinates(angle: number): void {
42
+ // this.atoms.rotate(angle);
43
+ // }
44
+
45
+ // getBondLines(): string[] {
46
+ // return this.bonds.getBondLines();
47
+ // }
48
+
49
+ // getAtomLines(): string[] {
50
+ // return this.atoms.atomLines;
51
+ // }
52
+
53
+ // removeRGroups(rGroupIds: number[]): void {
54
+ // this.rGroups.removeRGroups(rGroupIds);
55
+ // }
56
+
57
+ // replaceRGroupWithAttachmentAtom(rGroupId: number, externalAtom: number): void {
58
+ // this.rGroups.replaceRGroupWithAttachmentAtom(rGroupId, externalAtom);
59
+ // }
60
+
61
+ // getAttachmentAtomByRGroupId(rgroupId: number): number {
62
+ // return this.rGroups.getAttachmentAtomIdByRGroupId(rgroupId);
63
+ // }
64
+
65
+ // private shiftR1GroupToOrigin(): void {
66
+ // const r1Idx = this.rGroups.getAtomicIdx(1);
67
+ // if (r1Idx === null)
68
+ // throw new Error(`Cannot find R1 group for monomer ${this.monomerSymbol}`);
69
+ // const {x, y} = this.atoms.atomCoordinates[r1Idx];
70
+ // this.atoms.shift({x: -x, y: -y});
71
+ // }
72
+
73
+ // private alignR2AlongX(): void {
74
+ // const r2Idx = this.rGroups.getAtomicIdx(2);
75
+ // if (r2Idx === null)
76
+ // throw new Error(`Cannot find R2 group for monomer ${this.monomerSymbol}`);
77
+ // const r2Coordinates = this.atoms.atomCoordinates[r2Idx];
78
+ // const tan = r2Coordinates.y / r2Coordinates.x;
79
+ // const angle = Math.atan(tan);
80
+ // if (isNaN(angle))
81
+ // throw new Error(`Cannot calculate angle for R2 group for monomer ${this.monomerSymbol}`);
82
+ // this.rotateCoordinates(-angle);
83
+ // }
84
+
85
+ // private shiftMonomerToDefaultPosition(): void {
86
+ // this.shiftR1GroupToOrigin();
87
+ // const r2Idx = this.rGroups.getAtomicIdx(2);
88
+ // if (r2Idx !== null)
89
+ // this.alignR2AlongX();
90
+ // }
91
+
92
+ // shiftBonds(shift: number): void {
93
+ // this.bonds.shift(shift);
94
+ // }
95
+
96
+ // capRGroups(capGroupElements: string[]): void {
97
+ // this.rGroups.capRGroups(capGroupElements);
98
+ // }
99
+ // }
100
+
@@ -0,0 +1,21 @@
1
+ import {MolfileHandler} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler';
2
+ import {MolfileWrapper} from './mol-wrapper';
3
+ import {RGroupHandler} from './r-group-handler';
4
+ import {MolfileAtomsV2K} from './mol-atoms-v2k';
5
+ import {MolfileBondsV2K} from './mol-bonds-v2k';
6
+
7
+ export class MolfileV2KWrapper extends MolfileWrapper {
8
+ constructor(
9
+ molfileV2K: string, monomerSymbol: string
10
+ ) {
11
+ super(monomerSymbol);
12
+ const molfileHandler = MolfileHandler.getInstance(molfileV2K);
13
+
14
+ this.atoms = new MolfileAtomsV2K(molfileHandler);
15
+ this.bonds = new MolfileBondsV2K(molfileHandler);
16
+ this.rGroups = new RGroupHandler(molfileHandler, this.atoms, this.bonds);
17
+
18
+ this.shiftMonomerToDefaultPosition();
19
+ }
20
+ }
21
+
@@ -0,0 +1,21 @@
1
+ import {MolfileHandler} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler';
2
+ import {MolfileWrapper} from './mol-wrapper';
3
+ import {RGroupHandler} from './r-group-handler';
4
+ import {MolfileAtomsV3K} from './mol-atoms-v3k';
5
+ import {MolfileBondsV3K} from './mol-bonds-v3k';
6
+
7
+ export class MolfileV3KWrapper extends MolfileWrapper {
8
+ constructor(
9
+ molfileV3K: string, monomerSymbol: string
10
+ ) {
11
+ super(monomerSymbol);
12
+ const molfileHandler = MolfileHandler.getInstance(molfileV3K);
13
+
14
+ this.atoms = new MolfileAtomsV3K(molfileHandler);
15
+ this.bonds = new MolfileBondsV3K(molfileHandler);
16
+ this.rGroups = new RGroupHandler(molfileHandler, this.atoms, this.bonds);
17
+
18
+ this.shiftMonomerToDefaultPosition();
19
+ }
20
+ }
21
+
@@ -0,0 +1,79 @@
1
+ import {MolfileAtoms} from './mol-atoms';
2
+ import {MolfileBonds} from './mol-bonds';
3
+ import {RGroupHandler} from './r-group-handler';
4
+
5
+ export abstract class MolfileWrapper {
6
+ constructor(protected monomerSymbol: string) { }
7
+
8
+ protected atoms: MolfileAtoms;
9
+ protected bonds: MolfileBonds;
10
+ protected rGroups: RGroupHandler;
11
+
12
+ protected shiftR1GroupToOrigin(): void {
13
+ const r1Idx = this.rGroups.getAtomicIdx(1);
14
+ if (r1Idx === null)
15
+ throw new Error(`Cannot find R1 group for monomer ${this.monomerSymbol}`);
16
+ const {x, y} = this.atoms.atomCoordinates[r1Idx];
17
+ this.atoms.shift({x: -x, y: -y});
18
+ }
19
+
20
+ protected alignR2AlongX(): void {
21
+ const r2Idx = this.rGroups.getAtomicIdx(2);
22
+ if (r2Idx === null)
23
+ throw new Error(`Cannot find R2 group for monomer ${this.monomerSymbol}`);
24
+ const r2Coordinates = this.atoms.atomCoordinates[r2Idx];
25
+ const tan = r2Coordinates.y / r2Coordinates.x;
26
+ const angle = Math.atan(tan);
27
+ if (isNaN(angle))
28
+ throw new Error(`Cannot calculate angle for R2 group for monomer ${this.monomerSymbol}`);
29
+ this.rotateCoordinates(-angle);
30
+ }
31
+
32
+ protected shiftMonomerToDefaultPosition(): void {
33
+ this.shiftR1GroupToOrigin();
34
+ const r2Idx = this.rGroups.getAtomicIdx(2);
35
+ if (r2Idx !== null)
36
+ this.alignR2AlongX();
37
+ }
38
+
39
+ deleteBondLineWithSpecifiedRGroup(rGroupId: number): void {
40
+ this.rGroups.deleteBondLineWithSpecifiedRGroup(rGroupId);
41
+ }
42
+
43
+ shiftCoordinates(shift: {x: number, y: number}): void {
44
+ this.atoms.shift(shift);
45
+ }
46
+
47
+ rotateCoordinates(angle: number): void {
48
+ this.atoms.rotate(angle);
49
+ }
50
+
51
+ getBondLines(): string[] {
52
+ return this.bonds.getBondLines();
53
+ }
54
+
55
+ getAtomLines(): string[] {
56
+ return this.atoms.atomLines;
57
+ }
58
+
59
+ removeRGroups(rGroupIds: number[]): void {
60
+ this.rGroups.removeRGroups(rGroupIds);
61
+ }
62
+
63
+ replaceRGroupWithAttachmentAtom(rGroupId: number, externalAtom: number): void {
64
+ this.rGroups.replaceRGroupWithAttachmentAtom(rGroupId, externalAtom);
65
+ }
66
+
67
+ getAttachmentAtomByRGroupId(rgroupId: number): number {
68
+ return this.rGroups.getAttachmentAtomIdByRGroupId(rgroupId);
69
+ }
70
+
71
+ shiftBonds(shift: number): void {
72
+ this.bonds.shift(shift);
73
+ }
74
+
75
+ capRGroups(capGroupElements: string[]): void {
76
+ this.rGroups.capRGroups(capGroupElements);
77
+ }
78
+ }
79
+
@@ -0,0 +1,103 @@
1
+ import {Monomer} from '@datagrok-libraries/bio/src/types';
2
+ import {HELM_RGROUP_FIELDS} from '@datagrok-libraries/bio/src/utils/const';
3
+ import {RDModule} from '@datagrok-libraries/chem-meta/src/rdkit-api';
4
+ import {MonomerLibManager} from '../../monomer-lib/lib-manager';
5
+ import {Helm} from './helm';
6
+ import {MolfileWrapper} from './mol-wrapper';
7
+ import {MolfileWrapperFactory} from './mol-wrapper-factory';
8
+ import {MolfileHandler} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler';
9
+
10
+ export class MonomerWrapper {
11
+ private molfileWrapper: MolfileWrapper;
12
+ private capGroupElements: string[] = [];
13
+
14
+ constructor(
15
+ private monomerSymbol: string,
16
+ private monomerIdx: number,
17
+ private helm: Helm,
18
+ shift: {x: number, y: number},
19
+ rdKitModule: RDModule
20
+ ) {
21
+ const libraryMonomerObject = this.getLibraryMonomerObject();
22
+
23
+ let molfile = libraryMonomerObject.molfile;
24
+ if (MolfileHandler.isMolfileV2K(molfile))
25
+ molfile = this.convertMolfileToV3KFormat(molfile, rdKitModule);
26
+
27
+ this.molfileWrapper = MolfileWrapperFactory.getInstance(molfile, monomerSymbol);
28
+ this.capGroupElements = this.getCapGroupElements(libraryMonomerObject);
29
+
30
+ if (helm.bondedRGroupsMap.has(monomerIdx))
31
+ this.removeRGroups(helm.bondedRGroupsMap.get(monomerIdx)!);
32
+ this.capRemainingRGroups();
33
+
34
+ this.shiftCoordinates(shift);
35
+ }
36
+
37
+ private convertMolfileToV3KFormat(molfileV2K: string, rdKitModule: RDModule): string {
38
+ return rdKitModule.get_mol(molfileV2K, JSON.stringify({mergeQueryHs: true})).get_v3Kmolblock();
39
+ }
40
+
41
+ private getLibraryMonomerObject(): Monomer {
42
+ const polymerType = this.helm.getPolymerTypeByMonomerIdx(this.monomerIdx);
43
+ const monomerLib = MonomerLibManager.instance.getBioLib();
44
+ const monomer = monomerLib.getMonomer(polymerType, this.monomerSymbol);
45
+ if (!monomer)
46
+ throw new Error(`Monomer ${this.monomerSymbol} is not found in the library`);
47
+ return monomer;
48
+ }
49
+
50
+ private getCapGroupElements(
51
+ libraryMonomerObject: Monomer
52
+ ): string[] {
53
+ const rgroups = libraryMonomerObject.rgroups;
54
+ const result = rgroups.map((rgroup) => {
55
+ const smiles = rgroup[HELM_RGROUP_FIELDS.CAP_GROUP_SMILES] ||
56
+ // WARNING: ignore because both key variants coexist in HELM Core Library!
57
+ // @ts-ignore
58
+ rgroup[HELM_RGROUP_FIELDS.CAP_GROUP_SMILES_UPPERCASE];
59
+ // extract the element symbol
60
+ return smiles.replace(/(\[|\]|\*|:|\d)/g, '');
61
+ });
62
+
63
+ return result;
64
+ }
65
+
66
+ private shiftCoordinates(shift: {x: number, y: number}): void {
67
+ this.molfileWrapper.shiftCoordinates(shift);
68
+ }
69
+
70
+ getAtomLines(): string[] {
71
+ return this.molfileWrapper.getAtomLines();
72
+ }
73
+
74
+ getBondLines(): string[] {
75
+ return this.molfileWrapper.getBondLines();
76
+ }
77
+
78
+ private removeRGroups(rGroupIds: number[]): void {
79
+ this.molfileWrapper.removeRGroups(rGroupIds);
80
+ }
81
+
82
+ private capRemainingRGroups(): void {
83
+ this.molfileWrapper.capRGroups(this.capGroupElements);
84
+ }
85
+
86
+ replaceRGroupWithAttachmentAtom(rGroupId: number, attachmentAtomIdx: number): void {
87
+ this.molfileWrapper.replaceRGroupWithAttachmentAtom(rGroupId, attachmentAtomIdx);
88
+ };
89
+
90
+ getAttachmentAtomByRGroupId(rGroupId: number): number {
91
+ const attachmentAtom = this.molfileWrapper.getAttachmentAtomByRGroupId(rGroupId);
92
+ return attachmentAtom;
93
+ }
94
+
95
+ deleteBondLineWithSpecifiedRGroup(rGroupId: number): void {
96
+ this.molfileWrapper.deleteBondLineWithSpecifiedRGroup(rGroupId);
97
+ }
98
+
99
+ shiftBonds(shift: number): void {
100
+ this.molfileWrapper.shiftBonds(shift);
101
+ }
102
+ }
103
+
@@ -0,0 +1,99 @@
1
+ import {RDModule} from '@datagrok-libraries/chem-meta/src/rdkit-api';
2
+ import {V3K_CONST} from '@datagrok-libraries/chem-meta/src/formats/molfile-const';
3
+ import {Helm} from './helm';
4
+ import {MonomerWrapper} from './monomer-wrapper';
5
+
6
+ export class Polymer {
7
+ constructor(helmString: string, private rdKitModule: RDModule) {
8
+ this.helm = new Helm(helmString);
9
+ }
10
+
11
+ private monomerWrappers: MonomerWrapper[] = [];
12
+ private helm: Helm;
13
+
14
+ addMonomer(
15
+ monomerSymbol: string,
16
+ monomerIdx: number,
17
+ shift: {x: number, y: number},
18
+ ): void {
19
+ const monomerWrapper = new MonomerWrapper(monomerSymbol, monomerIdx, this.helm, shift, this.rdKitModule);
20
+
21
+ this.monomerWrappers.push(monomerWrapper);
22
+ }
23
+
24
+ private getAtomNumberShifts(): number[] {
25
+ const atomNumberShifts: number[] = [];
26
+ let shift = 0;
27
+ this.monomerWrappers.forEach((monomerWrapper) => {
28
+ atomNumberShifts.push(shift);
29
+ shift += monomerWrapper.getAtomLines().length;
30
+ });
31
+ return atomNumberShifts;
32
+ }
33
+
34
+ private restoreBondsBetweenMonomers(): void {
35
+ this.helm.bondData.forEach((bond) => {
36
+ const monomerIdx = bond.map((bondPart) => bondPart.monomerIdx);
37
+ const rGroupId = bond.map((bondPart) => bondPart.rGroupId);
38
+ const monomer = monomerIdx.map((idx) => this.monomerWrappers[idx]);
39
+
40
+ const attachmentAtom = monomer[1].getAttachmentAtomByRGroupId(rGroupId[1]);
41
+ monomer[0].replaceRGroupWithAttachmentAtom(rGroupId[0], attachmentAtom);
42
+ monomer[1].deleteBondLineWithSpecifiedRGroup(rGroupId[1]);
43
+ });
44
+ }
45
+
46
+ compileToMolfile(): string {
47
+ const atomLines: string[] = [];
48
+ const bondLines: string[] = [];
49
+
50
+ const atomNumberShifts = this.getAtomNumberShifts();
51
+ this.monomerWrappers.forEach((monomerWrapper, idx) => {
52
+ monomerWrapper.shiftBonds(atomNumberShifts[idx]);
53
+ });
54
+
55
+ this.restoreBondsBetweenMonomers();
56
+
57
+ this.monomerWrappers.forEach((monomerWrapper) => {
58
+ atomLines.push(...monomerWrapper.getAtomLines());
59
+ bondLines.push(...monomerWrapper.getBondLines());
60
+ });
61
+
62
+ const atomCount = atomLines.length;
63
+ const bondCount = bondLines.length;
64
+
65
+ const header = this.getV3KHeader(atomCount, bondCount);
66
+ const atomBlock = this.getV3KAtomBlock(atomLines);
67
+ const bondBlock = this.getV3KBondBlock(bondLines);
68
+ const molfileEnd = V3K_CONST.END_CTAB + '\n' + V3K_CONST.END;
69
+ const blockList = [header, atomBlock, bondBlock, molfileEnd];
70
+ const molfile = blockList.join('\n');
71
+ return molfile;
72
+ }
73
+
74
+ private getV3KHeader(atomCount: number, bondCount: number): string {
75
+ const countsLine = `${V3K_CONST.COUNTS_LINE_START}${atomCount} ${bondCount}${V3K_CONST.COUNTS_LINE_DUMMY_END}`;
76
+ return `${V3K_CONST.DUMMY_HEADER}\n${V3K_CONST.BEGIN_CTAB}\n${countsLine}`;
77
+ }
78
+
79
+ private getV3KAtomBlock(atomLines: string[]): string {
80
+ const regex = /^(M V30 )(\d+)( .*)$/;
81
+ const newAtomLines = atomLines.map((line, idx) => {
82
+ const atomIndex = idx + 1;
83
+ return line.replace(regex, (match, p1, p2, p3) => { return p1 + atomIndex + p3; });
84
+ });
85
+
86
+ const atomBlock = [V3K_CONST.BEGIN_ATOM_BLOCK, ...newAtomLines, V3K_CONST.END_ATOM_BLOCK];
87
+ return atomBlock.join('\n');
88
+ }
89
+
90
+ private getV3KBondBlock(bondLines: string[]): string {
91
+ const regex = /^(M V30 )(\d+)( .*)$/;
92
+ const newBondLines = bondLines.map((line, idx) => {
93
+ const atomIndex = idx + 1;
94
+ return line.replace(regex, (match, p1, p2, p3) => { return p1 + atomIndex + p3; });
95
+ });
96
+ const bondBlock = [V3K_CONST.BEGIN_BOND_BLOCK, ...newBondLines, V3K_CONST.END_BOND_BLOCK];
97
+ return bondBlock.join('\n');
98
+ }
99
+ }
@@ -0,0 +1,23 @@
1
+ import {MolfileHandler} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler';
2
+ import {MolfileHandlerBase} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler-base';
3
+
4
+ /** Extracts positions of vertices from pseudo-molfile (with
5
+ * monomers being the vertices) generated by HELM Web Editor */
6
+ export class GlobalMonomerPositionHandler {
7
+ constructor(helmCoordinatesPseudoMolfile: string) {
8
+ this.molfileHandler = MolfileHandler.getInstance(helmCoordinatesPseudoMolfile);
9
+ }
10
+
11
+ private molfileHandler: MolfileHandlerBase;
12
+
13
+ get monomerSymbols(): string[] {
14
+ return this.molfileHandler.atomTypes;
15
+ }
16
+
17
+ getMonomerShifts(monomerIdx: number): {x: number, y: number} {
18
+ const x = this.molfileHandler.x[monomerIdx];
19
+ const y = this.molfileHandler.y[monomerIdx];
20
+ return {x, y};
21
+ }
22
+ }
23
+
@@ -0,0 +1,122 @@
1
+ import {MolfileHandlerBase} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler-base';
2
+ import {HYDROGEN_SYMBOL} from './const';
3
+ import {MolfileAtoms} from './mol-atoms';
4
+ import {MolfileBonds} from './mol-bonds';
5
+ import {PositionInBonds} from './types';
6
+
7
+
8
+ export class RGroupHandler {
9
+ constructor(molfileHandler: MolfileHandlerBase, private atoms: MolfileAtoms, private bonds: MolfileBonds) {
10
+ this.rGroupIdToAtomicIndexMap = molfileHandler.getRGroupIdToAtomicIdxMap();
11
+ }
12
+
13
+ /** Relates R group id (starting from 1) to its atomic index within the
14
+ * molfile */
15
+ rGroupIdToAtomicIndexMap: Map<number, number>;
16
+
17
+ /** Maps R group id (starting from 1) to its position in the bond block */
18
+ private rGroupBondPositionMap = new Map<number, PositionInBonds>();
19
+
20
+ getAtomicIdx(rGroupId: number): number | null {
21
+ const atomicIdx = this.rGroupIdToAtomicIndexMap.get(rGroupId);
22
+ return atomicIdx === undefined ? null : atomicIdx;
23
+ }
24
+
25
+ private removeRGroupsFromAtomBlock(rGroupIds: number[]): void {
26
+ rGroupIds.forEach((rgroupId) => {
27
+ const atomicIdx = this.rGroupIdToAtomicIndexMap.get(rgroupId);
28
+ if (atomicIdx === undefined)
29
+ throw new Error(`Cannot find atomic index for R group ${rgroupId}`);
30
+ });
31
+
32
+ const rGroupAtomicIndices = Array.from(this.rGroupIdToAtomicIndexMap.entries()).filter(
33
+ ([rGroupId, _]) => rGroupIds.includes(rGroupId)
34
+ ).map(([_, atomicIdx]) => atomicIdx);
35
+ this.atoms.deleteAtoms(rGroupAtomicIndices);
36
+ }
37
+
38
+ removeRGroups(rGroupIds: number[]): void {
39
+ this.removeRGroupsFromAtomBlock(rGroupIds);
40
+
41
+ rGroupIds.forEach((rGroupId) => {
42
+ const dummyPosition = this.replaceRGroupInBondsByDummy(rGroupId);
43
+ this.rGroupBondPositionMap.set(rGroupId, dummyPosition);
44
+ });
45
+ }
46
+
47
+ /** Replace RGroups by -1, update associated maps, and return the position in
48
+ * bond block */
49
+ private replaceRGroupInBondsByDummy(rGroupId: number): PositionInBonds {
50
+ const rGroupAtomicIdx = this.rGroupIdToAtomicIndexMap.get(rGroupId)!;
51
+
52
+ if (this.rGroupBondPositionMap.has(rGroupId))
53
+ throw new Error(`R group ${rGroupId} is already handled`);
54
+
55
+ const positions = this.bonds.getPositionsInBonds(rGroupAtomicIdx + 1);
56
+ if (positions.length === 0)
57
+ throw new Error(`Cannot find position for R group ${rGroupId}`);
58
+ if (positions.length > 1)
59
+ throw new Error(`More than one position for R group ${rGroupId}`);
60
+
61
+ const rGroupPosition = positions[0];
62
+
63
+ this.bonds.replacePositionsInBondsByDummy([rGroupPosition]);
64
+ this.bonds.removeAtomIdFromBonds(rGroupAtomicIdx + 1);
65
+ this.removeRGroupFromAtomicIdxMap(rGroupId, rGroupAtomicIdx);
66
+
67
+ return rGroupPosition;
68
+ }
69
+
70
+ private removeRGroupFromAtomicIdxMap(deletedId: number, deletedAtomicIdx: number): void {
71
+ this.rGroupIdToAtomicIndexMap.delete(deletedId);
72
+ for (const [rGroupId, rGroupAtomicIdx] of this.rGroupIdToAtomicIndexMap) {
73
+ if (rGroupAtomicIdx > deletedAtomicIdx)
74
+ this.rGroupIdToAtomicIndexMap.set(rGroupId, rGroupAtomicIdx - 1);
75
+ }
76
+ }
77
+
78
+ deleteBondLineWithSpecifiedRGroup(rGroupId: number): void {
79
+ const position = this.rGroupBondPositionMap.get(rGroupId);
80
+ if (!position)
81
+ throw new Error(`Cannot find position for R group ${rGroupId}`);
82
+ const {bondLineIdx} = position;
83
+ this.bonds.deleteBondLines([bondLineIdx]);
84
+ this.rGroupBondPositionMap.delete(rGroupId);
85
+ this.rGroupIdToAtomicIndexMap.delete(rGroupId);
86
+ // update values of other positions
87
+ this.rGroupBondPositionMap.forEach((position) => {
88
+ if (position.bondLineIdx > bondLineIdx)
89
+ position.bondLineIdx -= 1;
90
+ });
91
+ }
92
+
93
+ replaceRGroupWithAttachmentAtom(rGroupId: number, externalAtom: number): void {
94
+ const position = this.rGroupBondPositionMap.get(rGroupId);
95
+ if (!position)
96
+ throw new Error(`Cannot find position for R group ${rGroupId}`);
97
+ const {bondLineIdx, nodeIdx} = position;
98
+ this.bonds.bondedAtoms[bondLineIdx][nodeIdx] = externalAtom;
99
+ }
100
+
101
+ /** Atom id is molfile id starting from 1 */
102
+ getAttachmentAtomIdByRGroupId(rgroupId: number): number {
103
+ const position = this.rGroupBondPositionMap.get(rgroupId);
104
+ if (!position)
105
+ throw new Error(`Cannot find position for R group ${rgroupId}`);
106
+ const {bondLineIdx, nodeIdx} = position;
107
+ return this.bonds.bondedAtoms[bondLineIdx][(nodeIdx + 1) % 2];
108
+ }
109
+
110
+ /** WARNING: capping RGroups and deletion of the bonded ones don't commute */
111
+ capRGroups(capGroupElements: string[]): void {
112
+ this.rGroupIdToAtomicIndexMap.forEach((atomicIdx, rGroupId) => {
113
+ const element = capGroupElements[rGroupId - 1];
114
+ if (element === HYDROGEN_SYMBOL) {
115
+ this.removeRGroups([rGroupId]);
116
+ this.deleteBondLineWithSpecifiedRGroup(rGroupId);
117
+ } else
118
+ this.atoms.replaceRGroupSymbolByElement(atomicIdx, element);
119
+ });
120
+ }
121
+ }
122
+