Skip to content

Commit 6230560

Browse files
authored
#4557 - Support of ambiguous monomers in small molecules mode (#5399)
- fixed ambiguous monomers in molecules mode - fixed rna builder for ambiguous monomers - fixed sequence mode for ambiguous monomers
1 parent ff8149c commit 6230560

File tree

15 files changed

+120
-55
lines changed

15 files changed

+120
-55
lines changed

Diff for: packages/ketcher-core/src/application/editor/modes/SequenceMode.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
import { AttachmentPointName, MonomerItemType } from 'domain/types';
1212
import { Command } from 'domain/entities/Command';
1313
import {
14+
AmbiguousMonomer,
1415
BaseMonomer,
1516
LinkerSequenceNode,
1617
Phosphate,
@@ -1438,7 +1439,7 @@ export class SequenceMode extends BaseMode {
14381439
position,
14391440
) as Sugar;
14401441

1441-
let rnaBaseMonomer: RNABase | null = null;
1442+
let rnaBaseMonomer: RNABase | AmbiguousMonomer | null = null;
14421443
if (rnaBase) {
14431444
rnaBaseMonomer = editor.drawingEntitiesManager.createMonomer(
14441445
rnaBase,

Diff for: packages/ketcher-core/src/application/editor/operations/monomer/monomerFactory.ts

+10-3
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import {
77
PhosphateRenderer,
88
UnresolvedMonomerRenderer,
99
UnsplitNucleotideRenderer,
10+
AmbiguousMonomerRenderer,
1011
} from 'application/render/renderers';
11-
import { MonomerItemType } from 'domain/types';
12+
import { MonomerOrAmbiguousType } from 'domain/types';
1213
import {
1314
Peptide,
1415
Chem,
@@ -17,8 +18,10 @@ import {
1718
RNABase,
1819
UnresolvedMonomer,
1920
UnsplitNucleotide,
21+
AmbiguousMonomer,
2022
} from 'domain/entities';
2123
import { KetMonomerClass } from 'application/formatters/types/ket';
24+
import { isAmbiguousMonomerLibraryItem } from 'domain/helpers/monomers';
2225

2326
type DerivedClass<T> = new (...args: unknown[]) => T;
2427
export const MONOMER_CONST = {
@@ -43,7 +46,7 @@ type Monomer =
4346
| typeof Phosphate;
4447

4548
export const monomerFactory = (
46-
monomer: MonomerItemType,
49+
monomer: MonomerOrAmbiguousType,
4750
): [
4851
Monomer: Monomer,
4952
MonomerRenderer: DerivedClass<BaseMonomerRenderer>,
@@ -53,7 +56,11 @@ export const monomerFactory = (
5356
let MonomerRenderer;
5457
let ketMonomerClass: KetMonomerClass;
5558

56-
if (monomer.props.unresolved) {
59+
if (isAmbiguousMonomerLibraryItem(monomer)) {
60+
Monomer = AmbiguousMonomer;
61+
MonomerRenderer = AmbiguousMonomerRenderer;
62+
ketMonomerClass = AmbiguousMonomer.getMonomerClass(monomer.monomers);
63+
} else if (monomer.props.unresolved) {
5764
Monomer = UnresolvedMonomer;
5865
MonomerRenderer = UnresolvedMonomerRenderer;
5966
ketMonomerClass = KetMonomerClass.CHEM;

Diff for: packages/ketcher-core/src/application/editor/tools/Monomer.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class MonomerTool implements BaseTool {
5757
),
5858
);
5959
if (isAmbiguousMonomerLibraryItem(this.monomer)) {
60-
modelChanges = this.editor.drawingEntitiesManager.addVariantMonomer(
60+
modelChanges = this.editor.drawingEntitiesManager.addAmbiguousMonomer(
6161
this.monomer,
6262
position,
6363
);

Diff for: packages/ketcher-core/src/application/render/renderers/AmbiguousMonomerRenderer.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ export class AmbiguousMonomerRenderer extends BaseMonomerRenderer {
109109
public show(theme) {
110110
super.show(theme);
111111
this.appendNumberOfMonomers();
112+
this.appendEnumeration();
112113
if (this.CHAIN_BEGINNING) {
113-
this.appendEnumeration();
114114
this.appendChainBeginning();
115115
}
116116
}

Diff for: packages/ketcher-core/src/application/render/renderers/RenderersManager.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ export class RenderersManager {
187187
const nextMonomer = getNextMonomerInChain(monomerRenderer.monomer);
188188

189189
if (
190-
!(nextMonomer instanceof Peptide) ||
190+
!isPeptideOrAmbiguousPeptide(nextMonomer) ||
191191
nextMonomer === peptideRenderer.monomer
192192
) {
193193
return;
@@ -311,10 +311,7 @@ export class RenderersManager {
311311
]);
312312

313313
this.monomers.forEach((monomerRenderer) => {
314-
if (
315-
monomerRenderer instanceof PeptideRenderer ||
316-
monomerRenderer instanceof AmbiguousMonomerRenderer
317-
) {
314+
if (isPeptideOrAmbiguousPeptide(monomerRenderer.monomer)) {
318315
this.recalculatePeptideEnumeration(
319316
monomerRenderer as PeptideRenderer,
320317
firstMonomersInCyclicChains,

Diff for: packages/ketcher-core/src/application/render/renderers/sequence/RNASequenceItemRenderer.ts

+34-6
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,43 @@
11
import { BaseSequenceItemRenderer } from 'application/render/renderers/sequence/BaseSequenceItemRenderer';
2-
import { Nucleoside, Nucleotide } from 'domain/entities';
2+
import {
3+
AmbiguousMonomer,
4+
Nucleoside,
5+
Nucleotide,
6+
Vec2,
7+
} from 'domain/entities';
8+
import { BaseSubChain } from 'domain/entities/monomer-chains/BaseSubChain';
39

410
export abstract class RNASequenceItemRenderer extends BaseSequenceItemRenderer {
5-
get symbolToDisplay(): string {
6-
return (
7-
this.node.monomer.attachmentPointsToBonds.R3?.getAnotherMonomer(
8-
this.node.monomer,
9-
)?.monomerItem?.props.MonomerNaturalAnalogCode || '@'
11+
constructor(
12+
public node: Nucleoside | Nucleotide,
13+
_firstNodeInChainPosition: Vec2,
14+
_monomerIndexInChain: number,
15+
_isLastMonomerInChain: boolean,
16+
_subChain: BaseSubChain,
17+
_isEditingSymbol: boolean,
18+
public monomerSize: { width: number; height: number },
19+
public scaledMonomerPosition: Vec2,
20+
) {
21+
super(
22+
node,
23+
_firstNodeInChainPosition,
24+
_monomerIndexInChain,
25+
_isLastMonomerInChain,
26+
_subChain,
27+
_isEditingSymbol,
28+
monomerSize,
29+
scaledMonomerPosition,
1030
);
1131
}
1232

33+
get symbolToDisplay(): string {
34+
return this.node.rnaBase instanceof AmbiguousMonomer
35+
? this.node.monomer.label
36+
: this.node.monomer.attachmentPointsToBonds.R3?.getAnotherMonomer(
37+
this.node.monomer,
38+
)?.monomerItem?.props.MonomerNaturalAnalogCode || '@';
39+
}
40+
1341
protected drawCommonModification(node: Nucleoside | Nucleotide) {
1442
if (node.rnaBase.isModification) {
1543
this.backgroundElement?.attr(

Diff for: packages/ketcher-core/src/domain/entities/DrawingEntitiesManager.ts

+31-19
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import {
5151
getNextMonomerInChain,
5252
getPhosphateFromSugar,
5353
isAmbiguousMonomerLibraryItem,
54+
isRnaBaseOrAmbiguousRnaBase,
5455
isValidNucleoside,
5556
isValidNucleotide,
5657
} from 'domain/helpers/monomers';
@@ -85,7 +86,7 @@ type RnaPresetAdditionParams = {
8586
type NucleotideOrNucleoside = {
8687
sugar: Sugar;
8788
phosphate?: Phosphate;
88-
rnaBase: RNABase;
89+
rnaBase: RNABase | AmbiguousMonomer;
8990
baseMonomer: Sugar | Phosphate;
9091
};
9192

@@ -938,27 +939,38 @@ export class DrawingEntitiesManager {
938939
let previousMonomer: BaseMonomer | undefined;
939940
const sugarMonomer = node.monomers.find(
940941
(monomer) => monomer instanceof Sugar,
941-
) as Sugar;
942+
) as Sugar | AmbiguousMonomer;
942943
const phosphateMonomer = node.monomers.find(
943944
(monomer) => monomer instanceof Phosphate,
944-
) as Phosphate;
945-
const rnaBaseMonomer = node.monomers.find(
946-
(monomer) => monomer instanceof RNABase,
947-
) as RNABase;
945+
) as Phosphate | AmbiguousMonomer;
946+
const rnaBaseMonomer = node.monomers.find((monomer) =>
947+
isRnaBaseOrAmbiguousRnaBase(monomer),
948+
) as RNABase | AmbiguousMonomer;
948949
const monomers = [rnaBaseMonomer, sugarMonomer, phosphateMonomer].filter(
949950
(monomer) => monomer !== undefined,
950951
) as BaseMonomer[];
951952

952953
monomers.forEach((monomer) => {
953-
const monomerAddOperation = new MonomerAddOperation(
954-
this.addMonomerChangeModel.bind(
955-
this,
956-
monomer.monomerItem,
957-
monomer.position,
958-
monomer,
959-
),
960-
this.deleteMonomerChangeModel.bind(this),
961-
);
954+
const monomerAddOperation =
955+
monomer instanceof AmbiguousMonomer
956+
? new MonomerAddOperation(
957+
this.addAmbiguousMonomerChangeModel.bind(
958+
this,
959+
monomer.variantMonomerItem,
960+
monomer.position,
961+
monomer,
962+
),
963+
this.deleteMonomerChangeModel.bind(this),
964+
)
965+
: new MonomerAddOperation(
966+
this.addMonomerChangeModel.bind(
967+
this,
968+
monomer.monomerItem,
969+
monomer.position,
970+
monomer,
971+
),
972+
this.deleteMonomerChangeModel.bind(this),
973+
);
962974

963975
command.addOperation(monomerAddOperation);
964976
if (previousMonomer) {
@@ -1513,7 +1525,7 @@ export class DrawingEntitiesManager {
15131525
this.monomers.forEach((monomer) => {
15141526
const monomerAddCommand =
15151527
monomer instanceof AmbiguousMonomer
1516-
? targetDrawingEntitiesManager.addVariantMonomer(
1528+
? targetDrawingEntitiesManager.addAmbiguousMonomer(
15171529
{
15181530
...monomer.variantMonomerItem,
15191531
},
@@ -1848,7 +1860,7 @@ export class DrawingEntitiesManager {
18481860
return command;
18491861
}
18501862

1851-
private addVariantMonomerChangeModel(
1863+
private addAmbiguousMonomerChangeModel(
18521864
variantMonomerItem: AmbiguousMonomerType,
18531865
position: Vec2,
18541866
_monomer?: BaseMonomer,
@@ -1866,13 +1878,13 @@ export class DrawingEntitiesManager {
18661878
return monomer;
18671879
}
18681880

1869-
public addVariantMonomer(
1881+
public addAmbiguousMonomer(
18701882
variantMonomerItem: AmbiguousMonomerType,
18711883
position: Vec2,
18721884
) {
18731885
const command = new Command();
18741886
const operation = new MonomerAddOperation(
1875-
this.addVariantMonomerChangeModel.bind(
1887+
this.addAmbiguousMonomerChangeModel.bind(
18761888
this,
18771889
variantMonomerItem,
18781890
position,

Diff for: packages/ketcher-core/src/domain/entities/Nucleoside.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@ import {
1717
getSugarBySequenceType,
1818
} from 'domain/helpers/rna';
1919
import { BaseMonomer } from 'domain/entities/BaseMonomer';
20+
import { AmbiguousMonomer } from 'domain/entities/AmbiguousMonomer';
2021

2122
export class Nucleoside {
22-
constructor(public sugar: Sugar, public rnaBase: RNABase) {}
23+
constructor(
24+
public sugar: Sugar,
25+
public rnaBase: RNABase | AmbiguousMonomer,
26+
) {}
2327

2428
static fromSugar(sugar: Sugar, needValidation = true) {
2529
if (needValidation) {

Diff for: packages/ketcher-core/src/domain/entities/Nucleotide.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,12 @@ import {
1717
} from 'domain/helpers/rna';
1818
import { RNA_DNA_NON_MODIFIED_PART } from 'domain/constants/monomers';
1919
import { BaseMonomer } from 'domain/entities/BaseMonomer';
20+
import { AmbiguousMonomer } from 'domain/entities/AmbiguousMonomer';
2021

2122
export class Nucleotide {
2223
constructor(
2324
public sugar: Sugar,
24-
public rnaBase: RNABase,
25+
public rnaBase: RNABase | AmbiguousMonomer,
2526
public phosphate: Phosphate,
2627
) {}
2728

Diff for: packages/ketcher-core/src/domain/serializers/ket/fromKet/monomerToDrawingEntity.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ export function variantMonomerToDrawingEntity(
8888

8989
const monomers = createMonomersForVariantMonomer(template, parsedFileContent);
9090

91-
return drawingEntitiesManager.addVariantMonomer(
91+
return drawingEntitiesManager.addAmbiguousMonomer(
9292
{
9393
monomers,
9494
id: template.id,

Diff for: packages/ketcher-core/src/domain/serializers/ket/ketSerializer.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ export class KetSerializer implements Serializer<Struct> {
644644
}
645645

646646
fileContent[templateNameWithPrefix] = {
647-
type: 'variantMonomerTemplate',
647+
type: 'ambiguousMonomerTemplate',
648648
id: templateId,
649649
alias: variantMonomer.label,
650650
idtAliases: variantMonomer.variantMonomerItem.idtAliases,

Diff for: packages/ketcher-macromolecules/src/components/monomerLibrary/RnaBuilder/RnaAccordion/RnaAccordion.tsx

+8-2
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,11 @@ import {
5858
} from 'state/rna-builder';
5959
import { useDispatch } from 'react-redux';
6060
import { IRnaPreset } from '../types';
61-
import { KetMonomerClass, MonomerItemType } from 'ketcher-core';
61+
import {
62+
isAmbiguousMonomerLibraryItem,
63+
KetMonomerClass,
64+
MonomerItemType,
65+
} from 'ketcher-core';
6266
import {
6367
selectEditor,
6468
selectIsSequenceEditInRNABuilderMode,
@@ -173,7 +177,9 @@ export const RnaAccordion = ({ libraryName, duplicatePreset, editPreset }) => {
173177
return;
174178
}
175179

176-
const monomerClass = monomer.props.MonomerClass.toLowerCase();
180+
const monomerClass = isAmbiguousMonomerLibraryItem(monomer)
181+
? monomer.monomers[0].monomerItem.props.MonomerClass?.toLowerCase()
182+
: monomer.props.MonomerClass.toLowerCase();
177183
const currentPreset = {
178184
...newPreset,
179185
[monomerClass]: monomer,

Diff for: packages/ketcher-macromolecules/src/helpers/getPreset.ts

+9-8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
setMonomerTemplatePrefix,
77
KetMonomerClass,
88
IRnaLabeledPreset,
9+
isAmbiguousMonomerLibraryItem,
10+
setAmbiguousMonomerTemplatePrefix,
911
} from 'ketcher-core';
1012
import { getMonomerUniqueKey } from 'state/library';
1113

@@ -20,7 +22,9 @@ export const getPresets = (
2022
): IRnaPreset[] => {
2123
const monomerLibraryItemByMonomerIDMap = new Map<string, MonomerItemType>(
2224
monomers.map((monomer) => {
23-
const monomerID = setMonomerTemplatePrefix(getMonomerUniqueKey(monomer));
25+
const monomerID = isAmbiguousMonomerLibraryItem(monomer)
26+
? setAmbiguousMonomerTemplatePrefix(monomer.id)
27+
: setMonomerTemplatePrefix(getMonomerUniqueKey(monomer));
2428
return [monomerID, monomer];
2529
}),
2630
);
@@ -37,6 +41,7 @@ export const getPresets = (
3741
rnaPartsMonomerTemplateRef.$ref,
3842
) as MonomerItemType;
3943
const [, , monomerClass] = monomerFactory(monomer);
44+
4045
return [monomerClass, monomer];
4146
}),
4247
);
@@ -53,16 +58,12 @@ export const getPresets = (
5358
) as MonomerItemType;
5459

5560
const result: IRnaPreset = {
56-
base: rnaBase
57-
? { ...rnaBase, label: rnaBase.props.MonomerName }
58-
: undefined,
61+
base: rnaBase ? { ...rnaBase, label: rnaBase.label } : undefined,
5962
name: rnaPresetsTemplate.name,
6063
phosphate: phosphate
61-
? { ...phosphate, label: phosphate.props.MonomerName }
62-
: undefined,
63-
sugar: ribose
64-
? { ...ribose, label: ribose.props.MonomerName }
64+
? { ...phosphate, label: phosphate.label }
6565
: undefined,
66+
sugar: ribose ? { ...ribose, label: ribose.label } : undefined,
6667
favorite: rnaPresetsTemplate.favorite,
6768
default: isDefault || rnaPresetsTemplate.default,
6869
};

Diff for: packages/ketcher-macromolecules/src/state/rna-builder/rnaBuilderSlice.helper.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import omit from 'lodash/omit';
22
import {
33
IRnaLabeledPreset,
44
IRnaPreset,
5+
setAmbiguousMonomerTemplatePrefix,
56
setMonomerTemplatePrefix,
67
} from 'ketcher-core';
78

@@ -15,9 +16,15 @@ export const transformRnaPresetToRnaLabeledPreset = (rnaPreset: IRnaPreset) => {
1516

1617
rnaLabeledPreset.templates = [];
1718
for (const monomerName of fieldsToLabel) {
18-
if (!rnaPreset[monomerName]?.props?.id) continue;
19+
const monomerLibraryItem = rnaPreset[monomerName];
20+
const templateId = monomerLibraryItem?.props?.id || monomerLibraryItem?.id;
21+
22+
if (!templateId) continue;
23+
1924
rnaLabeledPreset.templates.push({
20-
$ref: setMonomerTemplatePrefix(rnaPreset[monomerName].props.id),
25+
$ref: monomerLibraryItem.isAmbiguous
26+
? setAmbiguousMonomerTemplatePrefix(templateId)
27+
: setMonomerTemplatePrefix(templateId),
2128
});
2229
}
2330

0 commit comments

Comments
 (0)