From 187aedb387ca80b54054b431e953f578c0d8abf5 Mon Sep 17 00:00:00 2001 From: Starla Huang Date: Tue, 30 Jan 2024 18:12:58 +0800 Subject: [PATCH] #3869 enhancement --- .../Polymer-Bond-Tool/snake-bond-tool.spec.ts | 270 ++++++++++-------- .../tests/utils/canvas/helpers.ts | 10 + .../tests/utils/macromolecules/polymerBond.ts | 12 + .../src/application/editor/tools/RnaPreset.ts | 5 +- .../render/renderers/PolymerBondRenderer.ts | 23 +- .../src/domain/entities/BaseMonomer.ts | 4 + .../ketcher-core/src/domain/entities/Chem.ts | 8 + .../domain/entities/DrawingEntitiesManager.ts | 62 ++-- .../src/domain/entities/Peptide.ts | 8 + .../src/domain/entities/Phosphate.ts | 6 + .../ketcher-core/src/domain/entities/Sugar.ts | 6 + 11 files changed, 236 insertions(+), 178 deletions(-) diff --git a/ketcher-autotests/tests/Macromolecule-editor/Polymer-Bond-Tool/snake-bond-tool.spec.ts b/ketcher-autotests/tests/Macromolecule-editor/Polymer-Bond-Tool/snake-bond-tool.spec.ts index 3b57ac9830..850dd799b1 100644 --- a/ketcher-autotests/tests/Macromolecule-editor/Polymer-Bond-Tool/snake-bond-tool.spec.ts +++ b/ketcher-autotests/tests/Macromolecule-editor/Polymer-Bond-Tool/snake-bond-tool.spec.ts @@ -275,29 +275,35 @@ test.describe('Snake Bond Tool', () => { await page.getByText('RNA').click(); await selectSnakeBondTool(page); - await addRnaPresetOnCanvas(page, 'A_A_R_P', 300, 300); - await addRnaPresetOnCanvas(page, 'C_C_R_P', 400, 600); - await addRnaPresetOnCanvas(page, 'G_G_R_P', 600, 400); + const { phosphate } = await addRnaPresetOnCanvas( + page, + 'A_A_R_P', + 300, + 300, + 0, + 0, + ); + const { sugar: sugar1, phosphate: phosphate1 } = await addRnaPresetOnCanvas( + page, + 'C_C_R_P', + 400, + 600, + 1, + 1, + ); + const { sugar: sugar2 } = await addRnaPresetOnCanvas( + page, + 'G_G_R_P', + 600, + 400, + 2, + 2, + ); await selectSingleBondTool(page); - const phosphate1 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='P']]`) - .nth(0); - - const phosphate2 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='P']]`) - .nth(1); - - const sugar1 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='R']]`) - .nth(1); - const sugar2 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='R']]`) - .nth(2); - - await bondTwoMonomers(page, phosphate1, sugar1); - await bondTwoMonomers(page, phosphate2, sugar2); + await bondTwoMonomers(page, phosphate, sugar1); + await bondTwoMonomers(page, phosphate1, sugar2); await takeEditorScreenshot(page); }); @@ -305,43 +311,53 @@ test.describe('Snake Bond Tool', () => { test('Check snake mode arrange for RNA chain', async ({ page }) => { await page.getByText('RNA').click(); - await addRnaPresetOnCanvas(page, 'A_A_R_P', 300, 300); - await addRnaPresetOnCanvas(page, 'C_C_R_P', 400, 600); - await addRnaPresetOnCanvas(page, 'G_G_R_P', 600, 400); - await addRnaPresetOnCanvas(page, 'T_T_R_P', 800, 200); - await addRnaPresetOnCanvas(page, 'T_T_R_P', 100, 100); + const { phosphate } = await addRnaPresetOnCanvas( + page, + 'A_A_R_P', + 300, + 300, + 0, + 0, + ); + const { sugar: sugar1, phosphate: phosphate1 } = await addRnaPresetOnCanvas( + page, + 'C_C_R_P', + 400, + 600, + 1, + 1, + ); + const { sugar: sugar2, phosphate: phosphate2 } = await addRnaPresetOnCanvas( + page, + 'G_G_R_P', + 600, + 400, + 2, + 2, + ); + const { sugar: sugar3, phosphate: phosphate3 } = await addRnaPresetOnCanvas( + page, + 'T_T_R_P', + 800, + 200, + 3, + 3, + ); + const { sugar: sugar4 } = await addRnaPresetOnCanvas( + page, + 'T_T_R_P', + 100, + 100, + 4, + 4, + ); await selectSingleBondTool(page); - const phosphate1 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='P']]`) - .nth(0); - const phosphate2 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='P']]`) - .nth(1); - const phosphate3 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='P']]`) - .nth(2); - const phosphate4 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='P']]`) - .nth(3); - const sugar1 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='R']]`) - .nth(1); - const sugar2 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='R']]`) - .nth(2); - const sugar3 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='R']]`) - .nth(3); - const sugar4 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='R']]`) - .nth(4); - - await bondTwoMonomers(page, phosphate1, sugar1); - await bondTwoMonomers(page, phosphate2, sugar2); - await bondTwoMonomers(page, phosphate3, sugar3); - await bondTwoMonomers(page, phosphate4, sugar4); + await bondTwoMonomers(page, phosphate, sugar1); + await bondTwoMonomers(page, phosphate1, sugar2); + await bondTwoMonomers(page, phosphate2, sugar3); + await bondTwoMonomers(page, phosphate3, sugar4); await selectSnakeBondTool(page); await takeEditorScreenshot(page); @@ -411,35 +427,36 @@ test.describe('Snake Bond Tool', () => { await page.getByText('RNA').click(); await selectSnakeBondTool(page); - await addRnaPresetOnCanvas(page, 'A_A_R_P', 200, 200); - await addRnaPresetOnCanvas(page, 'C_C_R_P', 300, 500); - await addRnaPresetOnCanvas(page, 'G_G_R_P', 400, 300); + const { phosphate } = await addRnaPresetOnCanvas( + page, + 'A_A_R_P', + 200, + 200, + 0, + 0, + ); + const { sugar: sugar1, phosphate: phosphate1 } = await addRnaPresetOnCanvas( + page, + 'C_C_R_P', + 300, + 500, + 1, + 1, + ); + const { sugar: sugar2, phosphate: phosphate2 } = await addRnaPresetOnCanvas( + page, + 'G_G_R_P', + 400, + 300, + 2, + 2, + ); await selectSingleBondTool(page); - const phosphate1 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='P']]`) - .nth(0); - const phosphate2 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='P']]`) - .nth(1); - const phosphate3 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='P']]`) - .nth(2); - - const sugar1 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='R']]`) - .nth(1); - const sugar2 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='R']]`) - .nth(2); - - await bondTwoMonomers(page, phosphate1, sugar1); - await bondTwoMonomers(page, phosphate2, sugar2); - await bondTwoMonomers(page, phosphate3, peptide1); - - await page.locator('button[title=R1]').nth(1).click(); - await page.locator('button[title=Connect]').click(); + await bondTwoMonomers(page, phosphate, sugar1); + await bondTwoMonomers(page, phosphate1, sugar2); + await bondTwoMonomers(page, phosphate2, peptide1, undefined, 'R1'); await bondTwoMonomers(page, peptide1, peptide2); await bondTwoMonomers(page, peptide2, peptide3); @@ -460,8 +477,22 @@ test.describe('Snake Bond Tool', () => { await page.getByText('RNA').click(); await selectSnakeBondTool(page); - await addRnaPresetOnCanvas(page, 'A_A_R_P', 200, 200); - await addRnaPresetOnCanvas(page, 'G_G_R_P', 700, 300); + const { phosphate } = await addRnaPresetOnCanvas( + page, + 'A_A_R_P', + 200, + 200, + 0, + 0, + ); + const { sugar } = await addRnaPresetOnCanvas( + page, + 'G_G_R_P', + 700, + 300, + 1, + 1, + ); await page.getByTestId('summary-Sugars').click(); const sugarOfNucleoside = await addMonomerToCanvas( @@ -484,15 +515,6 @@ test.describe('Snake Bond Tool', () => { await selectSingleBondTool(page); await bondTwoMonomers(page, sugarOfNucleoside, baseOfNucleoside); - - const phosphate = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='P']]`) - .nth(0); - - const sugar = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='R']]`) - .nth(1); - await bondTwoMonomers(page, phosphate, sugarOfNucleoside); await bondTwoMonomers(page, sugarOfNucleoside, sugar); @@ -507,12 +529,31 @@ test.describe('Snake Bond Tool', () => { test('Create snake bond for chain with side chains', async ({ page }) => { await page.getByText('RNA').click(); - - await addRnaPresetOnCanvas(page, 'A_A_R_P', 200, 200); - await addRnaPresetOnCanvas(page, 'G_G_R_P', 500, 300); - await addRnaPresetOnCanvas(page, 'T_T_R_P', 700, 300); - await addRnaPresetOnCanvas(page, 'U_U_R_P', 900, 300); - + const { phosphate } = await addRnaPresetOnCanvas( + page, + 'A_A_R_P', + 200, + 200, + 0, + 0, + ); + const { sugar: sugar1, phosphate: phosphate1 } = await addRnaPresetOnCanvas( + page, + 'G_G_R_P', + 500, + 300, + 1, + 1, + ); + const { sugar: sugar2, phosphate: phosphate2 } = await addRnaPresetOnCanvas( + page, + 'T_T_R_P', + 700, + 300, + 2, + 2, + ); + await addRnaPresetOnCanvas(page, 'U_U_R_P', 900, 300, 3, 3); await page.getByTestId('summary-Sugars').click(); const sugarOfNucleoside = await addMonomerToCanvas( page, @@ -601,40 +642,17 @@ test.describe('Snake Bond Tool', () => { await selectSingleBondTool(page); await bondTwoMonomers(page, sugarOfNucleoside, baseOfNucleoside); - await bondTwoMonomers(page, baseOfNucleoside, monomer1); - await page.locator('button[title=R2]').nth(0).click(); - await page.locator('button[title=R1]').nth(1).click(); - await page.locator('button[title=Connect]').click(); + await bondTwoMonomers(page, baseOfNucleoside, monomer1, 'R2', 'R1'); await bondTwoMonomers(page, monomer1, monomer2); await bondTwoMonomers(page, monomer2, monomer3); await bondTwoMonomers(page, monomer3, monomer4); - await bondTwoMonomers(page, monomer2, monomer5); - await page.locator('button[title=R1]').nth(1).click(); - await page.locator('button[title=Connect]').click(); - - const phosphate = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='P']]`) - .nth(0); - - const sugar = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='R']]`) - .nth(1); - const sugar1 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='R']]`) - .nth(2); - const phosphate1 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='P']]`) - .nth(1); - const phosphate2 = await page - .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='P']]`) - .nth(2); + await bondTwoMonomers(page, monomer2, monomer5, undefined, 'R1'); + await bondTwoMonomers(page, phosphate, sugarOfNucleoside); - await bondTwoMonomers(page, sugarOfNucleoside, sugar); - await bondTwoMonomers(page, phosphate1, sugar1); - await bondTwoMonomers(page, phosphate2, monomer6); - await page.locator('button[title=R1]').nth(1).click(); - await page.locator('button[title=Connect]').click(); + await bondTwoMonomers(page, sugarOfNucleoside, sugar1); + await bondTwoMonomers(page, phosphate1, sugar2); + await bondTwoMonomers(page, phosphate2, monomer6, undefined, 'R1'); await bondTwoMonomers(page, monomer6, monomer7); await bondTwoMonomers(page, monomer7, monomer8); diff --git a/ketcher-autotests/tests/utils/canvas/helpers.ts b/ketcher-autotests/tests/utils/canvas/helpers.ts index fc9151ae81..d5c6f84be9 100644 --- a/ketcher-autotests/tests/utils/canvas/helpers.ts +++ b/ketcher-autotests/tests/utils/canvas/helpers.ts @@ -330,10 +330,20 @@ export async function addRnaPresetOnCanvas( presetId: string, positionX: number, positionY: number, + sugarIndex: number, + phosphateIndex: number, ) { await page.getByTestId(presetId).click(); await page.mouse.click(positionX, positionY); await hideMonomerPreview(page); + const sugar = await page + .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='R']]`) + .nth(sugarIndex); + const phosphate = await page + .locator(`//\*[name() = 'g' and ./\*[name()='text' and .='P']]`) + .nth(phosphateIndex); + + return { sugar, phosphate }; } export async function addChemOnCanvas(page: Page, chemId: string) { diff --git a/ketcher-autotests/tests/utils/macromolecules/polymerBond.ts b/ketcher-autotests/tests/utils/macromolecules/polymerBond.ts index 0b7eb58506..35d23ac75f 100644 --- a/ketcher-autotests/tests/utils/macromolecules/polymerBond.ts +++ b/ketcher-autotests/tests/utils/macromolecules/polymerBond.ts @@ -5,10 +5,22 @@ export async function bondTwoMonomers( page: Page, firstMonomerElement: Locator, secondMonomerElement: Locator, + connectTitle1?: string, + connectTitle2?: string, ) { await firstMonomerElement.hover(); await page.mouse.down(); await secondMonomerElement.hover(); await page.mouse.up(); await hideMonomerPreview(page); + const dialog = await page.getByRole('dialog'); + if (await dialog.isVisible()) { + if (connectTitle1) { + await page.locator(`button[title='${connectTitle1}']`).nth(0).click(); + } + if (connectTitle2) { + await page.locator(`button[title='${connectTitle2}']`).nth(1).click(); + } + await page.locator('button[title=Connect]').click(); + } } diff --git a/packages/ketcher-core/src/application/editor/tools/RnaPreset.ts b/packages/ketcher-core/src/application/editor/tools/RnaPreset.ts index 4d85cb41a0..2cb6ea281c 100644 --- a/packages/ketcher-core/src/application/editor/tools/RnaPreset.ts +++ b/packages/ketcher-core/src/application/editor/tools/RnaPreset.ts @@ -25,6 +25,7 @@ import { RNABase } from 'domain/entities/RNABase'; import { Phosphate } from 'domain/entities/Phosphate'; import { Coordinates } from '../shared/coordinates'; +export const RNA_MONOMER_DISTANCE = 45; class RnaPresetTool implements Tool { rnaBase: MonomerItemType | undefined; sugar: MonomerItemType | undefined; @@ -78,7 +79,7 @@ class RnaPresetTool implements Tool { new Vec2( this.editor.lastCursorPositionOfCanvas.x + this.sugarPreviewRenderer?.width + - 45, + RNA_MONOMER_DISTANCE, this.editor.lastCursorPositionOfCanvas.y, ), ) @@ -90,7 +91,7 @@ class RnaPresetTool implements Tool { this.editor.lastCursorPositionOfCanvas.x, this.editor.lastCursorPositionOfCanvas.y + this.sugarPreviewRenderer.height + - 45, + RNA_MONOMER_DISTANCE, ), ) : undefined, diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer.ts index 8d1d430dd0..1935450c4b 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer.ts @@ -45,30 +45,15 @@ export class PolymerBondRenderer extends BaseRenderer { ); } - private isMonomersTypeDifferent() { - return ( - (this.polymerBond.secondMonomer && - [Peptide, Chem].some( - (type) => this.polymerBond.firstMonomer instanceof type, - ) && - [Sugar, Phosphate].some( - (type) => this.polymerBond.secondMonomer instanceof type, - )) || - ([Peptide, Chem].some( - (type) => this.polymerBond.secondMonomer instanceof type, - ) && - [Sugar, Phosphate].some( - (type) => this.polymerBond.firstMonomer instanceof type, - )) - ); - } - get isSnake() { if ( !this.isSnakeBondAvailableForMonomer(this.polymerBond.firstMonomer) || (this.polymerBond.secondMonomer && !this.isSnakeBondAvailableForMonomer(this.polymerBond.secondMonomer)) || - this.isMonomersTypeDifferent() + (this.polymerBond.secondMonomer && + this.polymerBond.firstMonomer.isMonomerTypeDifferentForSnakeMode( + this.polymerBond.secondMonomer, + )) ) { return false; } diff --git a/packages/ketcher-core/src/domain/entities/BaseMonomer.ts b/packages/ketcher-core/src/domain/entities/BaseMonomer.ts index f25bf95ff8..1191b70ff9 100644 --- a/packages/ketcher-core/src/domain/entities/BaseMonomer.ts +++ b/packages/ketcher-core/src/domain/entities/BaseMonomer.ts @@ -444,4 +444,8 @@ export abstract class BaseMonomer extends DrawingEntity { return this.firstFreeAttachmentPoint; } + + public isMonomerTypeDifferentForSnakeMode(_monomerToChain: BaseMonomer) { + return true; + } } diff --git a/packages/ketcher-core/src/domain/entities/Chem.ts b/packages/ketcher-core/src/domain/entities/Chem.ts index 9c7f9efe3a..b702e71d88 100644 --- a/packages/ketcher-core/src/domain/entities/Chem.ts +++ b/packages/ketcher-core/src/domain/entities/Chem.ts @@ -1,5 +1,7 @@ import { BaseMonomer } from 'domain/entities/BaseMonomer'; import { Peptide } from 'domain/entities/Peptide'; +import { Sugar } from './Sugar'; +import { Phosphate } from './Phosphate'; export class Chem extends BaseMonomer { public getValidSourcePoint(monomer?: BaseMonomer) { @@ -9,4 +11,10 @@ export class Chem extends BaseMonomer { public getValidTargetPoint(monomer: BaseMonomer) { return Peptide.prototype.getValidTargetPoint.call(this, monomer); } + + public isMonomerTypeDifferentForSnakeMode(monomerToChain: BaseMonomer) { + return ( + monomerToChain instanceof Sugar || monomerToChain instanceof Phosphate + ); + } } diff --git a/packages/ketcher-core/src/domain/entities/DrawingEntitiesManager.ts b/packages/ketcher-core/src/domain/entities/DrawingEntitiesManager.ts index 891374e6cd..3df916153c 100644 --- a/packages/ketcher-core/src/domain/entities/DrawingEntitiesManager.ts +++ b/packages/ketcher-core/src/domain/entities/DrawingEntitiesManager.ts @@ -43,6 +43,7 @@ import { getNextMonomerInChain, getRnaBaseMonomerFromSugar, } from 'domain/helpers/monomers'; +import { RNA_MONOMER_DISTANCE } from 'application/editor/tools/RnaPreset'; const HORIZONTAL_DISTANCE_FROM_MONOMER = 50; const VERTICAL_DISTANCE_FROM_MONOMER = 60; @@ -853,11 +854,11 @@ export class DrawingEntitiesManager { const width = (nucleotide.sugar.renderer?.monomerSize.width || 0) + (nucleotide.phosphate?.renderer?.monomerSize.width || 0) + - (nucleotide.phosphate ? 45 : 0); + (nucleotide.phosphate ? RNA_MONOMER_DISTANCE : 0); const height = (nucleotide.sugar.renderer?.monomerSize.height || 0) + (nucleotide.rnaBase.renderer?.monomerSize.height || 0) + - 45; + RNA_MONOMER_DISTANCE; return { width, height }; } @@ -922,7 +923,7 @@ export class DrawingEntitiesManager { lastPosition.y + (nucleotide.sugar.renderer?.monomerSize?.height ?? 0) / 2 + (nucleotide.rnaBase.renderer?.monomerSize?.height ?? 0) / 2 + - 45, + RNA_MONOMER_DISTANCE, ); this.addRnaOperations( command, @@ -945,7 +946,7 @@ export class DrawingEntitiesManager { lastPosition.x + (nucleotide.sugar.renderer?.monomerSize?.width ?? 0) / 2 + (nucleotide.phosphate?.renderer?.monomerSize?.width ?? 0) / 2 + - 45, + RNA_MONOMER_DISTANCE, lastPosition.y, ); this.addRnaOperations( @@ -956,16 +957,11 @@ export class DrawingEntitiesManager { ); rearrangedMonomersSet.add(nucleotide.phosphate?.id); } - this.getRnaBaseSideChainMonomers( - nucleotide.rnaBase, - rearrangedMonomersSet, - monomersWithSideChain, - ); const nextMonomer = nucleotide.baseMonomer === nucleotide.sugar && nucleotide.phosphate ? nucleotide.phosphate : nucleotide.sugar; - return this.reArrangeNextMonomer( + const nextMonomerResult = this.reArrangeNextMonomer( nextMonomer, width, lastPosition, @@ -975,6 +971,15 @@ export class DrawingEntitiesManager { maxVerticalDistance, command, ); + command.merge(nextMonomerResult.command); + ({ lastPosition, maxVerticalDistance } = nextMonomerResult); + + this.getRnaBaseSideChainMonomers( + nucleotide.rnaBase, + rearrangedMonomersSet, + monomersWithSideChain, + ); + return { command, lastPosition, maxVerticalDistance }; } private reArrangeNextMonomer( @@ -989,25 +994,16 @@ export class DrawingEntitiesManager { ) { for (const attachmentPointName in monomer.attachmentPointsToBonds) { const polymerBond = monomer.attachmentPointsToBonds[attachmentPointName]; - const nextMonomer = - polymerBond?.secondMonomer === monomer - ? polymerBond?.firstMonomer - : polymerBond?.secondMonomer; + const nextMonomer = polymerBond?.getAnotherMonomer(monomer); if (!polymerBond || rearrangedMonomersSet.has(nextMonomer.id)) { continue; } if ( - !( - attachmentPointName === 'R2' && - nextMonomer.getAttachmentPointByBond(polymerBond) === 'R1' - ) && - !( - attachmentPointName === 'R1' && - nextMonomer.getAttachmentPointByBond(polymerBond) === 'R2' - ) + (attachmentPointName === 'R2' && + nextMonomer.getAttachmentPointByBond(polymerBond) === 'R1') || + (attachmentPointName === 'R1' && + nextMonomer.getAttachmentPointByBond(polymerBond) === 'R2') ) { - monomersWithSideChain.push(nextMonomer); - } else { ({ lastPosition, maxVerticalDistance } = this.getNextPositionAndDistance( lastPosition, @@ -1025,6 +1021,8 @@ export class DrawingEntitiesManager { ); ({ lastPosition, maxVerticalDistance } = rearrangeResult); command.merge(rearrangeResult.command); + } else { + monomersWithSideChain.push(nextMonomer); } } return { command, lastPosition, maxVerticalDistance }; @@ -1035,12 +1033,11 @@ export class DrawingEntitiesManager { rearrangedMonomersSet: Set, monomersWithSideChain: Array, ) { - for (const attachmentPointName in rnaBase.attachmentPointsToBonds) { + for (const attachmentPointName of Object.keys( + rnaBase.attachmentPointsToBonds, + ).reverse()) { const polymerBond = rnaBase.attachmentPointsToBonds[attachmentPointName]; - const nextMonomer = - polymerBond?.secondMonomer === rnaBase - ? polymerBond?.firstMonomer - : polymerBond?.secondMonomer; + const nextMonomer = polymerBond?.getAnotherMonomer(rnaBase); if ( !polymerBond || !nextMonomer || @@ -1269,7 +1266,7 @@ export class DrawingEntitiesManager { lastPosition, ); if (monomersWithSideChain.length > 0) { - monomersWithSideChain.forEach((monomerWithSideChain) => { + monomersWithSideChain.reverse().forEach((monomerWithSideChain) => { const currentMonomerChain: BaseMonomer[] = this.findChainByMonomer( monomerWithSideChain, undefined, @@ -1279,7 +1276,10 @@ export class DrawingEntitiesManager { [Peptide, Chem, Sugar, Phosphate], currentMonomerChain, ); - if (!firstMonomers.length) { + if ( + !firstMonomers.length && + !rearrangedMonomersSet.has(monomerWithSideChain.id) + ) { firstMonomers = [monomerWithSideChain]; } const rearrangeResult = this.reArrangeMonomers( diff --git a/packages/ketcher-core/src/domain/entities/Peptide.ts b/packages/ketcher-core/src/domain/entities/Peptide.ts index b62f34adf0..af589d5607 100644 --- a/packages/ketcher-core/src/domain/entities/Peptide.ts +++ b/packages/ketcher-core/src/domain/entities/Peptide.ts @@ -1,4 +1,6 @@ import { BaseMonomer } from './BaseMonomer'; +import { Phosphate } from './Phosphate'; +import { Sugar } from './Sugar'; export class Peptide extends BaseMonomer { public getValidSourcePoint(secondMonomer?: BaseMonomer) { @@ -76,4 +78,10 @@ export class Peptide extends BaseMonomer { return undefined; } + + public isMonomerTypeDifferentForSnakeMode(monomerToChain: BaseMonomer) { + return ( + monomerToChain instanceof Sugar || monomerToChain instanceof Phosphate + ); + } } diff --git a/packages/ketcher-core/src/domain/entities/Phosphate.ts b/packages/ketcher-core/src/domain/entities/Phosphate.ts index 2e9ca827f4..f7f922294c 100644 --- a/packages/ketcher-core/src/domain/entities/Phosphate.ts +++ b/packages/ketcher-core/src/domain/entities/Phosphate.ts @@ -2,6 +2,8 @@ import { BaseMonomer } from './BaseMonomer'; import { MonomerItemType } from 'domain/types'; import { Vec2 } from './vec2'; import { Sugar } from './Sugar'; +import { Peptide } from './Peptide'; +import { Chem } from './Chem'; export class Phosphate extends BaseMonomer { constructor(monomerItem: MonomerItemType, _position?: Vec2) { @@ -79,4 +81,8 @@ export class Phosphate extends BaseMonomer { return undefined; } + + public isMonomerTypeDifferentForSnakeMode(monomerToChain: BaseMonomer) { + return monomerToChain instanceof Peptide || monomerToChain instanceof Chem; + } } diff --git a/packages/ketcher-core/src/domain/entities/Sugar.ts b/packages/ketcher-core/src/domain/entities/Sugar.ts index c497b70c20..6922f1b039 100644 --- a/packages/ketcher-core/src/domain/entities/Sugar.ts +++ b/packages/ketcher-core/src/domain/entities/Sugar.ts @@ -1,6 +1,8 @@ import { BaseMonomer } from './BaseMonomer'; import { RNABase } from './RNABase'; import { Phosphate } from './Phosphate'; +import { Peptide } from './Peptide'; +import { Chem } from './Chem'; export class Sugar extends BaseMonomer { public getValidSourcePoint(secondMonomer: BaseMonomer) { @@ -90,4 +92,8 @@ export class Sugar extends BaseMonomer { return undefined; } + + public isMonomerTypeDifferentForSnakeMode(monomerToChain: BaseMonomer) { + return monomerToChain instanceof Peptide || monomerToChain instanceof Chem; + } }