Skip to content

Commit

Permalink
#4557 - Support of ambiguous monomers in small molecules mode (#5399)
Browse files Browse the repository at this point in the history
- fixed ambiguous monomers in molecules mode
- fixed rna builder for ambiguous monomers
- fixed sequence mode for ambiguous monomers
  • Loading branch information
rrodionov91 committed Sep 1, 2024
1 parent 919ec5c commit 8f94e77
Show file tree
Hide file tree
Showing 15 changed files with 120 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { AttachmentPointName, MonomerItemType } from 'domain/types';
import { Command } from 'domain/entities/Command';
import {
AmbiguousMonomer,
BaseMonomer,
LinkerSequenceNode,
Phosphate,
Expand Down Expand Up @@ -1438,7 +1439,7 @@ export class SequenceMode extends BaseMode {
position,
) as Sugar;

let rnaBaseMonomer: RNABase | null = null;
let rnaBaseMonomer: RNABase | AmbiguousMonomer | null = null;
if (rnaBase) {
rnaBaseMonomer = editor.drawingEntitiesManager.createMonomer(
rnaBase,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import {
PhosphateRenderer,
UnresolvedMonomerRenderer,
UnsplitNucleotideRenderer,
AmbiguousMonomerRenderer,
} from 'application/render/renderers';
import { MonomerItemType } from 'domain/types';
import { MonomerOrAmbiguousType } from 'domain/types';
import {
Peptide,
Chem,
Expand All @@ -17,8 +18,10 @@ import {
RNABase,
UnresolvedMonomer,
UnsplitNucleotide,
AmbiguousMonomer,
} from 'domain/entities';
import { KetMonomerClass } from 'application/formatters/types/ket';
import { isAmbiguousMonomerLibraryItem } from 'domain/helpers/monomers';

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

export const monomerFactory = (
monomer: MonomerItemType,
monomer: MonomerOrAmbiguousType,
): [
Monomer: Monomer,
MonomerRenderer: DerivedClass<BaseMonomerRenderer>,
Expand All @@ -53,7 +56,11 @@ export const monomerFactory = (
let MonomerRenderer;
let ketMonomerClass: KetMonomerClass;

if (monomer.props.unresolved) {
if (isAmbiguousMonomerLibraryItem(monomer)) {
Monomer = AmbiguousMonomer;
MonomerRenderer = AmbiguousMonomerRenderer;
ketMonomerClass = AmbiguousMonomer.getMonomerClass(monomer.monomers);
} else if (monomer.props.unresolved) {
Monomer = UnresolvedMonomer;
MonomerRenderer = UnresolvedMonomerRenderer;
ketMonomerClass = KetMonomerClass.CHEM;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class MonomerTool implements BaseTool {
),
);
if (isAmbiguousMonomerLibraryItem(this.monomer)) {
modelChanges = this.editor.drawingEntitiesManager.addVariantMonomer(
modelChanges = this.editor.drawingEntitiesManager.addAmbiguousMonomer(
this.monomer,
position,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ export class AmbiguousMonomerRenderer extends BaseMonomerRenderer {
public show(theme) {
super.show(theme);
this.appendNumberOfMonomers();
this.appendEnumeration();
if (this.CHAIN_BEGINNING) {
this.appendEnumeration();
this.appendChainBeginning();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ export class RenderersManager {
const nextMonomer = getNextMonomerInChain(monomerRenderer.monomer);

if (
!(nextMonomer instanceof Peptide) ||
!isPeptideOrAmbiguousPeptide(nextMonomer) ||
nextMonomer === peptideRenderer.monomer
) {
return;
Expand Down Expand Up @@ -311,10 +311,7 @@ export class RenderersManager {
]);

this.monomers.forEach((monomerRenderer) => {
if (
monomerRenderer instanceof PeptideRenderer ||
monomerRenderer instanceof AmbiguousMonomerRenderer
) {
if (isPeptideOrAmbiguousPeptide(monomerRenderer.monomer)) {
this.recalculatePeptideEnumeration(
monomerRenderer as PeptideRenderer,
firstMonomersInCyclicChains,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,43 @@
import { BaseSequenceItemRenderer } from 'application/render/renderers/sequence/BaseSequenceItemRenderer';
import { Nucleoside, Nucleotide } from 'domain/entities';
import {
AmbiguousMonomer,
Nucleoside,
Nucleotide,
Vec2,
} from 'domain/entities';
import { BaseSubChain } from 'domain/entities/monomer-chains/BaseSubChain';

export abstract class RNASequenceItemRenderer extends BaseSequenceItemRenderer {
get symbolToDisplay(): string {
return (
this.node.monomer.attachmentPointsToBonds.R3?.getAnotherMonomer(
this.node.monomer,
)?.monomerItem?.props.MonomerNaturalAnalogCode || '@'
constructor(
public node: Nucleoside | Nucleotide,
_firstNodeInChainPosition: Vec2,
_monomerIndexInChain: number,
_isLastMonomerInChain: boolean,
_subChain: BaseSubChain,
_isEditingSymbol: boolean,
public monomerSize: { width: number; height: number },
public scaledMonomerPosition: Vec2,
) {
super(
node,
_firstNodeInChainPosition,
_monomerIndexInChain,
_isLastMonomerInChain,
_subChain,
_isEditingSymbol,
monomerSize,
scaledMonomerPosition,
);
}

get symbolToDisplay(): string {
return this.node.rnaBase instanceof AmbiguousMonomer
? this.node.monomer.label
: this.node.monomer.attachmentPointsToBonds.R3?.getAnotherMonomer(
this.node.monomer,
)?.monomerItem?.props.MonomerNaturalAnalogCode || '@';
}

protected drawCommonModification(node: Nucleoside | Nucleotide) {
if (node.rnaBase.isModification) {
this.backgroundElement?.attr(
Expand Down
50 changes: 31 additions & 19 deletions packages/ketcher-core/src/domain/entities/DrawingEntitiesManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
getNextMonomerInChain,
getPhosphateFromSugar,
isAmbiguousMonomerLibraryItem,
isRnaBaseOrAmbiguousRnaBase,
isValidNucleoside,
isValidNucleotide,
} from 'domain/helpers/monomers';
Expand Down Expand Up @@ -85,7 +86,7 @@ type RnaPresetAdditionParams = {
type NucleotideOrNucleoside = {
sugar: Sugar;
phosphate?: Phosphate;
rnaBase: RNABase;
rnaBase: RNABase | AmbiguousMonomer;
baseMonomer: Sugar | Phosphate;
};

Expand Down Expand Up @@ -938,27 +939,38 @@ export class DrawingEntitiesManager {
let previousMonomer: BaseMonomer | undefined;
const sugarMonomer = node.monomers.find(
(monomer) => monomer instanceof Sugar,
) as Sugar;
) as Sugar | AmbiguousMonomer;
const phosphateMonomer = node.monomers.find(
(monomer) => monomer instanceof Phosphate,
) as Phosphate;
const rnaBaseMonomer = node.monomers.find(
(monomer) => monomer instanceof RNABase,
) as RNABase;
) as Phosphate | AmbiguousMonomer;
const rnaBaseMonomer = node.monomers.find((monomer) =>
isRnaBaseOrAmbiguousRnaBase(monomer),
) as RNABase | AmbiguousMonomer;
const monomers = [rnaBaseMonomer, sugarMonomer, phosphateMonomer].filter(
(monomer) => monomer !== undefined,
) as BaseMonomer[];

monomers.forEach((monomer) => {
const monomerAddOperation = new MonomerAddOperation(
this.addMonomerChangeModel.bind(
this,
monomer.monomerItem,
monomer.position,
monomer,
),
this.deleteMonomerChangeModel.bind(this),
);
const monomerAddOperation =
monomer instanceof AmbiguousMonomer
? new MonomerAddOperation(
this.addAmbiguousMonomerChangeModel.bind(
this,
monomer.variantMonomerItem,
monomer.position,
monomer,
),
this.deleteMonomerChangeModel.bind(this),
)
: new MonomerAddOperation(
this.addMonomerChangeModel.bind(
this,
monomer.monomerItem,
monomer.position,
monomer,
),
this.deleteMonomerChangeModel.bind(this),
);

command.addOperation(monomerAddOperation);
if (previousMonomer) {
Expand Down Expand Up @@ -1513,7 +1525,7 @@ export class DrawingEntitiesManager {
this.monomers.forEach((monomer) => {
const monomerAddCommand =
monomer instanceof AmbiguousMonomer
? targetDrawingEntitiesManager.addVariantMonomer(
? targetDrawingEntitiesManager.addAmbiguousMonomer(
{
...monomer.variantMonomerItem,
},
Expand Down Expand Up @@ -1848,7 +1860,7 @@ export class DrawingEntitiesManager {
return command;
}

private addVariantMonomerChangeModel(
private addAmbiguousMonomerChangeModel(
variantMonomerItem: AmbiguousMonomerType,
position: Vec2,
_monomer?: BaseMonomer,
Expand All @@ -1866,13 +1878,13 @@ export class DrawingEntitiesManager {
return monomer;
}

public addVariantMonomer(
public addAmbiguousMonomer(
variantMonomerItem: AmbiguousMonomerType,
position: Vec2,
) {
const command = new Command();
const operation = new MonomerAddOperation(
this.addVariantMonomerChangeModel.bind(
this.addAmbiguousMonomerChangeModel.bind(
this,
variantMonomerItem,
position,
Expand Down
6 changes: 5 additions & 1 deletion packages/ketcher-core/src/domain/entities/Nucleoside.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ import {
getSugarBySequenceType,
} from 'domain/helpers/rna';
import { BaseMonomer } from 'domain/entities/BaseMonomer';
import { AmbiguousMonomer } from 'domain/entities/AmbiguousMonomer';

export class Nucleoside {
constructor(public sugar: Sugar, public rnaBase: RNABase) {}
constructor(
public sugar: Sugar,
public rnaBase: RNABase | AmbiguousMonomer,
) {}

static fromSugar(sugar: Sugar, needValidation = true) {
if (needValidation) {
Expand Down
3 changes: 2 additions & 1 deletion packages/ketcher-core/src/domain/entities/Nucleotide.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ import {
} from 'domain/helpers/rna';
import { RNA_DNA_NON_MODIFIED_PART } from 'domain/constants/monomers';
import { BaseMonomer } from 'domain/entities/BaseMonomer';
import { AmbiguousMonomer } from 'domain/entities/AmbiguousMonomer';

export class Nucleotide {
constructor(
public sugar: Sugar,
public rnaBase: RNABase,
public rnaBase: RNABase | AmbiguousMonomer,
public phosphate: Phosphate,
) {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export function variantMonomerToDrawingEntity(

const monomers = createMonomersForVariantMonomer(template, parsedFileContent);

return drawingEntitiesManager.addVariantMonomer(
return drawingEntitiesManager.addAmbiguousMonomer(
{
monomers,
id: template.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ export class KetSerializer implements Serializer<Struct> {
}

fileContent[templateNameWithPrefix] = {
type: 'variantMonomerTemplate',
type: 'ambiguousMonomerTemplate',
id: templateId,
alias: variantMonomer.label,
idtAliases: variantMonomer.variantMonomerItem.idtAliases,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ import {
} from 'state/rna-builder';
import { useDispatch } from 'react-redux';
import { IRnaPreset } from '../types';
import { KetMonomerClass, MonomerItemType } from 'ketcher-core';
import {
isAmbiguousMonomerLibraryItem,
KetMonomerClass,
MonomerItemType,
} from 'ketcher-core';
import {
selectEditor,
selectIsSequenceEditInRNABuilderMode,
Expand Down Expand Up @@ -173,7 +177,9 @@ export const RnaAccordion = ({ libraryName, duplicatePreset, editPreset }) => {
return;
}

const monomerClass = monomer.props.MonomerClass.toLowerCase();
const monomerClass = isAmbiguousMonomerLibraryItem(monomer)
? monomer.monomers[0].monomerItem.props.MonomerClass?.toLowerCase()
: monomer.props.MonomerClass.toLowerCase();
const currentPreset = {
...newPreset,
[monomerClass]: monomer,
Expand Down
17 changes: 9 additions & 8 deletions packages/ketcher-macromolecules/src/helpers/getPreset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
setMonomerTemplatePrefix,
KetMonomerClass,
IRnaLabeledPreset,
isAmbiguousMonomerLibraryItem,
setAmbiguousMonomerTemplatePrefix,
} from 'ketcher-core';
import { getMonomerUniqueKey } from 'state/library';

Expand All @@ -20,7 +22,9 @@ export const getPresets = (
): IRnaPreset[] => {
const monomerLibraryItemByMonomerIDMap = new Map<string, MonomerItemType>(
monomers.map((monomer) => {
const monomerID = setMonomerTemplatePrefix(getMonomerUniqueKey(monomer));
const monomerID = isAmbiguousMonomerLibraryItem(monomer)
? setAmbiguousMonomerTemplatePrefix(monomer.id)
: setMonomerTemplatePrefix(getMonomerUniqueKey(monomer));
return [monomerID, monomer];
}),
);
Expand All @@ -37,6 +41,7 @@ export const getPresets = (
rnaPartsMonomerTemplateRef.$ref,
) as MonomerItemType;
const [, , monomerClass] = monomerFactory(monomer);

return [monomerClass, monomer];
}),
);
Expand All @@ -53,16 +58,12 @@ export const getPresets = (
) as MonomerItemType;

const result: IRnaPreset = {
base: rnaBase
? { ...rnaBase, label: rnaBase.props.MonomerName }
: undefined,
base: rnaBase ? { ...rnaBase, label: rnaBase.label } : undefined,
name: rnaPresetsTemplate.name,
phosphate: phosphate
? { ...phosphate, label: phosphate.props.MonomerName }
: undefined,
sugar: ribose
? { ...ribose, label: ribose.props.MonomerName }
? { ...phosphate, label: phosphate.label }
: undefined,
sugar: ribose ? { ...ribose, label: ribose.label } : undefined,
favorite: rnaPresetsTemplate.favorite,
default: isDefault || rnaPresetsTemplate.default,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import omit from 'lodash/omit';
import {
IRnaLabeledPreset,
IRnaPreset,
setAmbiguousMonomerTemplatePrefix,
setMonomerTemplatePrefix,
} from 'ketcher-core';

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

rnaLabeledPreset.templates = [];
for (const monomerName of fieldsToLabel) {
if (!rnaPreset[monomerName]?.props?.id) continue;
const monomerLibraryItem = rnaPreset[monomerName];
const templateId = monomerLibraryItem?.props?.id || monomerLibraryItem?.id;

if (!templateId) continue;

rnaLabeledPreset.templates.push({
$ref: setMonomerTemplatePrefix(rnaPreset[monomerName].props.id),
$ref: monomerLibraryItem.isAmbiguous
? setAmbiguousMonomerTemplatePrefix(templateId)
: setMonomerTemplatePrefix(templateId),
});
}

Expand Down
Loading

0 comments on commit 8f94e77

Please sign in to comment.