Skip to content

Commit

Permalink
#5403 - Introduce hydrogen bonds in macromolecules mode (#5843)
Browse files Browse the repository at this point in the history
- created BaseBond for PolymerBond and MonomerToAtomBond.
- added hydrogen bond renderer
- added hydrogen bonds serialization/deserialization and displaying in sequence and molecules modes
- added hiding of hydrogen bonds for expanded monomers
  • Loading branch information
rrodionov91 authored Oct 28, 2024
1 parent 4364600 commit c55cc66
Show file tree
Hide file tree
Showing 79 changed files with 531 additions and 223 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('Polymer Bond Renderer', () => {
it('should render bond', () => {
const canvas = createPolymerEditorCanvas();
const polymerBond = getFinishedPolymerBond(10, 10, 90, 100);
polymerBond.moveToLinkedMonomers();
polymerBond.moveToLinkedEntities();
const polymerBondRenderer =
polymerBond.renderer as FlexModeOrSnakeModePolymerBondRenderer;
global.SVGElement.prototype.getBBox = jest.fn();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { RenderersManager } from 'application/render/renderers/RenderersManager'
import { createPolymerEditorCanvas } from '../../helpers/dom';
import { CoreEditor } from 'application/editor';
import { FlexMode } from 'application/editor/modes/FlexMode';
import { MACROMOLECULES_BOND_TYPES } from 'application/editor/tools/Bond';

describe('Drawing Entities Manager', () => {
it('should create monomer', () => {
Expand All @@ -42,6 +43,7 @@ describe('Drawing Entities Manager', () => {
new Peptide(peptideMonomerItem),
new Vec2(0, 0),
new Vec2(10, 10),
MACROMOLECULES_BOND_TYPES.SINGLE,
);
expect(command.operations.length).toEqual(1);
expect(command.operations[0]).toBeInstanceOf(PolymerBondAddOperation);
Expand All @@ -64,6 +66,7 @@ describe('Drawing Entities Manager', () => {
firstPeptide,
new Vec2(0, 0),
new Vec2(10, 10),
MACROMOLECULES_BOND_TYPES.SINGLE,
);

const resultingOperations =
Expand Down Expand Up @@ -118,6 +121,7 @@ describe('Drawing Entities Manager', () => {
new Peptide(peptideMonomerItem),
new Vec2(0, 0),
new Vec2(10, 10),
MACROMOLECULES_BOND_TYPES.SINGLE,
);
expect(
Array.from(drawingEntitiesManager.polymerBonds)[0][1],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe('Polymer Bond', () => {
createPolymerEditorCanvas();
const polymerBond = getFinishedPolymerBond(0.25, 0.25, 2.25, 2.5);

polymerBond.moveToLinkedMonomers();
polymerBond.moveToLinkedEntities();

expect(polymerBond.finished).toBe(true);
expect(polymerBond.startPosition.x).toBe(0.25);
Expand Down
12 changes: 8 additions & 4 deletions packages/ketcher-core/src/application/editor/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import { Coordinates } from './shared/coordinates';
import ZoomTool from './tools/Zoom';
import { ViewModel } from 'application/render/view-model/ViewModel';
import { HandTool } from 'application/editor/tools/Hand';
import { HydrogenBond } from 'domain/entities/HydrogenBond';

interface ICoreEditorConstructorParams {
theme;
Expand Down Expand Up @@ -223,7 +224,8 @@ export class CoreEditor {
);
} else if (
eventData instanceof FlexModePolymerBondRenderer ||
eventData instanceof SnakeModePolymerBondRenderer
(eventData instanceof SnakeModePolymerBondRenderer &&
!(eventData.polymerBond instanceof HydrogenBond))
) {
this.events.rightClickPolymerBond.dispatch(event, eventData);
} else if (isClickOnCanvas) {
Expand All @@ -237,7 +239,9 @@ export class CoreEditor {
private subscribeEvents() {
this.events.selectMonomer.add((monomer) => this.onSelectMonomer(monomer));
this.events.selectPreset.add((preset) => this.onSelectRNAPreset(preset));
this.events.selectTool.add((tool) => this.onSelectTool(tool));
this.events.selectTool.add((tool, options) =>
this.onSelectTool(tool, options),
);
this.events.createBondViaModal.add((payload) => this.onCreateBond(payload));
this.events.cancelBondCreationViaModal.add((secondMonomer: BaseMonomer) =>
this.onCancelBondCreation(secondMonomer),
Expand Down Expand Up @@ -315,8 +319,8 @@ export class CoreEditor {
}
}

public onSelectTool(tool: ToolName) {
this.selectTool(tool);
public onSelectTool(tool: ToolName, options?: object) {
this.selectTool(tool, options);
}

private onCreateBond(payload: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import {
AmbiguousMonomer,
Atom as MicromoleculesAtom,
Bond,
FunctionalGroup,
Pile,
SGroup,
SGroupAttachmentPoint,
Struct,
Vec2,
Atom as MicromoleculesAtom,
} from 'domain/entities';
import { DrawingEntitiesManager } from 'domain/entities/DrawingEntitiesManager';
import { ReAtom, ReBond, ReSGroup, ReStruct } from 'application/render';
Expand All @@ -29,6 +29,8 @@ import { CoreEditor } from 'application/editor/Editor';
import { Atom } from 'domain/entities/CoreAtom';
import { AtomLabel } from 'domain/constants';
import { isMonomerSgroupWithAttachmentPoints } from '../../utilities/monomers';
import { HydrogenBond } from 'domain/entities/HydrogenBond';
import { MACROMOLECULES_BOND_TYPES } from 'application/editor/tools/Bond';

export class MacromoleculesConverter {
private static convertMonomerToMonomerMicromolecule(
Expand Down Expand Up @@ -113,6 +115,10 @@ export class MacromoleculesConverter {
reStruct?: ReStruct,
) {
const monomerToAtomIdMap = new Map<BaseMonomer, Map<number, number>>();
const monomerToMonomerMicromolecule = new Map<
BaseMonomer,
MonomerMicromolecule
>();

drawingEntitiesManager.micromoleculesHiddenEntities.mergeInto(struct);

Expand Down Expand Up @@ -148,6 +154,8 @@ export class MacromoleculesConverter {
? monomer.monomers[0].monomerItem.struct.bonds
: monomer.monomerItem.struct.bonds;

monomerToMonomerMicromolecule.set(monomer, monomerMicromolecule);

monomerAtoms.forEach((oldAtom, oldAtomId) => {
const { atom, atomId } = this.addMonomerAtomToStruct(
oldAtom,
Expand Down Expand Up @@ -196,6 +204,26 @@ export class MacromoleculesConverter {

drawingEntitiesManager.polymerBonds.forEach((polymerBond) => {
assert(polymerBond.secondMonomer);

if (polymerBond instanceof HydrogenBond) {
const bond = new Bond({
type: Bond.PATTERN.TYPE.HYDROGEN,
begin: monomerToAtomIdMap
.get(polymerBond.firstMonomer)
?.values()
.next().value,
end: monomerToAtomIdMap
.get(polymerBond.secondMonomer)
?.values()
.next().value,
});
const bondId = struct.bonds.add(bond);

reStruct?.bonds.set(bondId, new ReBond(bond));

return;
}

const {
globalAttachmentAtomId: beginAtom,
attachmentPointNumber: beginSuperatomAttachmentPointNumber,
Expand All @@ -221,7 +249,10 @@ export class MacromoleculesConverter {
}

const bond = new Bond({
type: Bond.PATTERN.TYPE.SINGLE,
type:
polymerBond instanceof HydrogenBond
? Bond.PATTERN.TYPE.HYDROGEN
: Bond.PATTERN.TYPE.SINGLE,
begin: beginAtom,
end: endAtom,
beginSuperatomAttachmentPointNumber,
Expand Down Expand Up @@ -619,6 +650,9 @@ export class MacromoleculesConverter {
secondMonomer,
getAttachmentPointLabel(beginAtomAttachmentPointNumber),
getAttachmentPointLabel(endAtomAttachmentPointNumber),
bond.type === Bond.PATTERN.TYPE.HYDROGEN
? MACROMOLECULES_BOND_TYPES.HYDROGEN
: MACROMOLECULES_BOND_TYPES.SINGLE,
),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { ChemicalMimeType } from 'domain/services';
import { PolymerBond } from 'domain/entities/PolymerBond';
import { ketcherProvider } from 'application/utils';
import { DrawingEntitiesManager } from 'domain/entities/DrawingEntitiesManager';
import { HydrogenBond } from 'domain/entities/HydrogenBond';

export abstract class BaseMode {
private _pasteIsInProgress = false;
Expand Down Expand Up @@ -110,7 +111,10 @@ export abstract class BaseMode {
entity.position,
entity,
);
} else if (entity instanceof PolymerBond && entity.secondMonomer) {
} else if (
(entity instanceof PolymerBond || entity instanceof HydrogenBond) &&
entity.secondMonomer
) {
const firstAttachmentPoint =
entity.firstMonomer.getAttachmentPointByBond(entity);
const secondAttachmentPoint =
Expand All @@ -126,6 +130,7 @@ export abstract class BaseMode {
entity.secondMonomer,
firstAttachmentPoint,
secondAttachmentPoint,
undefined,
entity,
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { RenderersManager } from 'application/render/renderers/RenderersManager';
import { Operation } from 'domain/entities/Operation';
import { DrawingEntity } from 'domain/entities/DrawingEntity';
import { PolymerBond } from 'domain/entities/PolymerBond';
import { MonomerToAtomBond } from 'domain/entities/MonomerToAtomBond';
import { BaseBond } from 'domain/entities/BaseBond';
export class DrawingEntityHoverOperation implements Operation {
constructor(private drawingEntity: DrawingEntity) {}

Expand Down Expand Up @@ -40,10 +39,7 @@ export class DrawingEntityMoveOperation implements Operation {
public invert(renderersManager: RenderersManager) {
this.invertMoveDrawingEntityChangeModel();

if (
this.drawingEntity instanceof PolymerBond ||
this.drawingEntity instanceof MonomerToAtomBond
) {
if (this.drawingEntity instanceof BaseBond) {
renderersManager.redrawDrawingEntity(this.drawingEntity);
} else {
renderersManager.moveDrawingEntity(this.drawingEntity);
Expand All @@ -57,10 +53,7 @@ export class DrawingEntityMoveOperation implements Operation {
// they have two drawing modes: straight and curved.
// During switching snake/flex layout modes and undo/redo
// we need to redraw them to apply the correct drawing mode.
if (
this.drawingEntity instanceof PolymerBond ||
this.drawingEntity instanceof MonomerToAtomBond
) {
if (this.drawingEntity instanceof BaseBond) {
renderersManager.redrawDrawingEntity(this.drawingEntity);
} else {
renderersManager.moveDrawingEntity(this.drawingEntity);
Expand Down
64 changes: 50 additions & 14 deletions packages/ketcher-core/src/application/editor/tools/Bond.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,41 @@ import { AttachmentPointName } from 'domain/types';
// because of using uncontrolled `index.ts` files.
import { Coordinates } from '../shared/coordinates';
import { AtomRenderer } from 'application/render/renderers/AtomRenderer';
import { ToolName } from 'application/editor';

type FlexModeOrSnakeModePolymerBondRenderer =
| FlexModePolymerBondRenderer
| SnakeModePolymerBondRenderer;

export enum MACROMOLECULES_BOND_TYPES {
SINGLE = 'single',
HYDROGEN = 'hydrogen',
}

class PolymerBond implements BaseTool {
private bondRenderer?: FlexModeOrSnakeModePolymerBondRenderer;
private isBondConnectionModalOpen = false;
history: EditorHistory;
private history: EditorHistory;
private bondType: MACROMOLECULES_BOND_TYPES;

constructor(private editor: CoreEditor) {
constructor(private editor: CoreEditor, options: { toolName: ToolName }) {
this.editor = editor;
this.history = new EditorHistory(this.editor);
this.bondType =
options.toolName === ToolName.bondSingle
? MACROMOLECULES_BOND_TYPES.SINGLE
: MACROMOLECULES_BOND_TYPES.HYDROGEN;
}

get isHydrogenBond() {
return this.bondType === MACROMOLECULES_BOND_TYPES.HYDROGEN;
}

public mouseDownAttachmentPoint(event) {
if (this.isHydrogenBond) {
return;
}

const selectedRenderer = event.target.__data__;
if (
selectedRenderer instanceof AttachmentPoint &&
Expand Down Expand Up @@ -96,6 +115,7 @@ class PolymerBond implements BaseTool {
selectedRenderer.monomer,
selectedRenderer.monomer.position,
Coordinates.canvasToModel(this.editor.lastCursorPositionOfCanvas),
this.bondType,
);

this.editor.renderersContainer.update(modelChanges);
Expand Down Expand Up @@ -159,7 +179,7 @@ class PolymerBond implements BaseTool {
this.editor.drawingEntitiesManager.intendToFinishBondCreation(
renderer.monomer,
this.bondRenderer?.polymerBond,
shouldCalculateBonds,
this.isHydrogenBond ? false : shouldCalculateBonds,
);
} else {
modelChanges =
Expand All @@ -173,6 +193,10 @@ class PolymerBond implements BaseTool {
}

public mouseOverAttachmentPoint(event) {
if (this.isHydrogenBond) {
return;
}

const renderer: AttachmentPoint = event.target.__data__;
let modelChanges;

Expand Down Expand Up @@ -323,17 +347,22 @@ class PolymerBond implements BaseTool {
this.bondRenderer.polymerBond,
);
}
const firstMonomerAttachmentPoint =
this.bondRenderer.polymerBond.firstMonomer.getPotentialAttachmentPointByBond(
this.bondRenderer.polymerBond,
);
const secondMonomerAttachmentPoint =
secondMonomer.getPotentialAttachmentPointByBond(
this.bondRenderer.polymerBond,
);
const firstMonomerAttachmentPoint = this.isHydrogenBond
? AttachmentPointName.HYDROGEN
: this.bondRenderer.polymerBond.firstMonomer.getPotentialAttachmentPointByBond(
this.bondRenderer.polymerBond,
);
const secondMonomerAttachmentPoint = this.isHydrogenBond
? AttachmentPointName.HYDROGEN
: secondMonomer.getPotentialAttachmentPointByBond(
this.bondRenderer.polymerBond,
);
assert(firstMonomerAttachmentPoint);
assert(secondMonomerAttachmentPoint);
if (firstMonomerAttachmentPoint === secondMonomerAttachmentPoint) {
if (
firstMonomerAttachmentPoint === secondMonomerAttachmentPoint &&
!this.isHydrogenBond
) {
this.editor.events.error.dispatch(
'You have connected monomers with attachment points of the same group',
);
Expand All @@ -342,7 +371,10 @@ class PolymerBond implements BaseTool {
this.bondRenderer.polymerBond,
secondMonomer,
firstMonomerAttachmentPoint,
secondMonomerAttachmentPoint,
this.isHydrogenBond
? AttachmentPointName.HYDROGEN
: secondMonomerAttachmentPoint,
this.bondType,
);
}

Expand Down Expand Up @@ -408,7 +440,7 @@ class PolymerBond implements BaseTool {
}

public mouseUpAtom(event) {
if (!this.bondRenderer) {
if (!this.bondRenderer || this.isHydrogenBond) {
return;
}

Expand Down Expand Up @@ -507,6 +539,10 @@ class PolymerBond implements BaseTool {
secondMonomer: BaseMonomer,
checkForPotentialBonds = true,
) {
if (this.isHydrogenBond) {
return;
}

// No Modal: no free attachment point on second monomer
if (!secondMonomer.hasFreeAttachmentPoint) {
return false;
Expand Down
Loading

0 comments on commit c55cc66

Please sign in to comment.