diff --git a/packages/ketcher-core/src/domain/entities/sgroup.ts b/packages/ketcher-core/src/domain/entities/sgroup.ts index 7d3d90ecee..1c1cd08ace 100644 --- a/packages/ketcher-core/src/domain/entities/sgroup.ts +++ b/packages/ketcher-core/src/domain/entities/sgroup.ts @@ -214,6 +214,23 @@ export class SGroup { this.pp = topLeftPoint } + getAttAtomId(struct: Struct): number { + for (const atomId of this.atoms) { + const atom = struct.atoms.get(atomId) + if (!atom) continue + if (Number.isInteger(atom.attpnt)) return atomId + } + // in normal circumstances this should never be invoked + return this.atoms[0] + } + + isGroupAttached(struct: Struct): boolean { + const attachPointId = this.getAttAtomId(struct) + const neighbours = struct.atomGetNeighbors(attachPointId) + + return !neighbours?.every(({ aid }) => this.atoms.includes(aid)) + } + static getOffset(sgroup: SGroup): null | Vec2 { if (!sgroup?.pp) return null return Vec2.diff(sgroup.pp, sgroup.bracketBox.p1) diff --git a/packages/ketcher-react/src/script/editor/tool/template.ts b/packages/ketcher-react/src/script/editor/tool/template.ts index 34915b4792..fed03d8617 100644 --- a/packages/ketcher-react/src/script/editor/tool/template.ts +++ b/packages/ketcher-react/src/script/editor/tool/template.ts @@ -254,14 +254,14 @@ class TemplateTool { const dragCtx = this.dragCtx const ci = dragCtx.item - let pos0: Vec2 | null | undefined = null - const pos1 = this.editor.render.page2obj(event) + let targetPos: Vec2 | null | undefined = null + const eventPos = this.editor.render.page2obj(event) const struct = restruct.molecule /* moving when attached to bond */ if (ci && ci.map === 'bonds' && this.mode !== 'fg') { const bond = struct.bonds.get(ci.id) - let sign = getSign(struct, bond, pos1) + let sign = getSign(struct, bond, eventPos) if (dragCtx.sign1 * this.template.sign > 0) { sign = -sign @@ -296,21 +296,22 @@ class TemplateTool { // calc initial pos and is extra bond needed if (!ci) { // ci.type == 'Canvas' - pos0 = dragCtx.xy0 + targetPos = dragCtx.xy0 } else if (ci.map === 'atoms') { - pos0 = struct.atoms.get(ci.id)?.pp + targetPos = struct.atoms.get(ci.id)?.pp - if (pos0) { - extraBond = this.mode === 'fg' ? true : Vec2.dist(pos0, pos1) > 1 + if (targetPos) { + extraBond = + this.mode === 'fg' ? true : Vec2.dist(targetPos, eventPos) > 1 } } - if (!pos0) { + if (!targetPos) { return true } // calc angle - let angle = utils.calcAngle(pos0, pos1) + let angle = utils.calcAngle(targetPos, eventPos) if (!event.ctrlKey) { angle = utils.fracAngle(angle, null) @@ -373,9 +374,8 @@ class TemplateTool { delete this.dragCtx const restruct = this.editor.render.ctab - const sgroups = restruct.sgroups const struct = restruct.molecule - const ci = dragCtx.item + let ci = dragCtx.item const functionalGroups = struct.functionalGroups /* after moving around bond */ @@ -402,7 +402,6 @@ class TemplateTool { let action let pasteItems = null - let isFunctionalGroupReplace = false if (SGroup.isSaltOrSolvent(this.template.molecule.name)) { addSaltsAndSolventsOnCanvasWithoutMerge( @@ -417,18 +416,47 @@ class TemplateTool { FunctionalGroup.isContractedFunctionalGroup(ci.id, functionalGroups) && this.mode === 'fg' ) { - const sGroup = sgroups.get(ci.id) + const closestGroup = this.editor.struct().sgroups.get(ci.id) + const isClosestGroupAttached = + closestGroup && closestGroup.isGroupAttached(this.editor.struct()) + + if (isClosestGroupAttached) { + const groupAttachmentAtomId = this.editor + .struct() + .atoms.find((atomId) => { + return !!this.editor + .struct() + .atomGetNeighbors(atomId) + ?.find( + (neighbor) => + neighbor.aid === + closestGroup.getAttAtomId(this.editor.struct()) + ) + }) + + if (groupAttachmentAtomId !== null) { + const targetPos = + this.editor.struct().atoms.get(groupAttachmentAtomId)?.pp || + dragCtx.xy0 + const eventPos = this.editor.render.page2obj(event) + + const dist = Vec2.dist(targetPos, eventPos) + ci = { map: 'atoms', dist, id: groupAttachmentAtomId } + } + } + this.editor.update( fromFragmentDeletion(this.editor.render.ctab, { - atoms: [...SGroup.getAtoms(struct, sGroup?.item)], - bonds: [...SGroup.getBonds(struct, sGroup?.item)] + atoms: [...SGroup.getAtoms(struct, closestGroup)], + bonds: [...SGroup.getBonds(struct, closestGroup)] }) ) - isFunctionalGroupReplace = true + + if (!isClosestGroupAttached) ci = null } if (!dragCtx.action) { - if (!ci || isFunctionalGroupReplace) { + if (!ci) { // ci.type == 'Canvas' ;[action, pasteItems] = fromTemplateOnCanvas( restruct,