Skip to content

Commit

Permalink
#2110 - Drag-n-drop fix for group interaction
Browse files Browse the repository at this point in the history
  • Loading branch information
Stanislav Permiakov authored and Stanislav Permiakov committed Feb 6, 2023
1 parent f65b5fd commit e07976e
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export function fromItemsFuse(restruct, items) {

const connectedAtomIds = getAllConnectedAtomsIds(
restruct,
mergeMapOfAtomsToSet(items.atoms),
mergeMapOfAtomsToSet(items.bonds)
mergeMapOfItemsToSet(items.atoms),
mergeMapOfItemsToSet(items.bonds)
)

// merge single atoms
Expand Down Expand Up @@ -73,6 +73,14 @@ export function getHoverToFuse(items) {
return { map: 'merge', id: +Date.now(), items: hoverItems }
}

export function mergeMapOfItemsToSet(items: Map<number, number>): Set<number> {
const itemsSet = new Set<number>()
items.forEach((value, key) => {
itemsSet.add(value).add(key)
})
return itemsSet
}

/**
* @param struct
* @param closestMap {{
Expand Down Expand Up @@ -107,14 +115,6 @@ function closestToMerge(struct, closestMap) {
return mergeMap
}

function mergeMapOfAtomsToSet(items: Map<number, number>): Set<number> {
const itemsSet = new Set<number>()
items.forEach((value, key) => {
itemsSet.add(value).add(key)
})
return itemsSet
}

function getAllConnectedAtomsIds(restruct, atomsIds, bondsIds) {
const initialAtoms = new Set(atomsIds)
const connectedAtoms = new Set()
Expand Down
28 changes: 28 additions & 0 deletions packages/ketcher-core/src/domain/entities/struct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ export class Struct {
)
}

isSingleGroup(): boolean {
if (!this.sgroups.size || this.sgroups.size > 1) return false
const sgroup = Array.from(this.sgroups)[0][1]
return this.atoms.size === sgroup.atoms.length
}

clone(
atomSet?: Pile<number> | null,
bondSet?: Pile<number> | null,
Expand Down Expand Up @@ -1064,4 +1070,26 @@ export class Struct {
}
})
}

isAtomBelongToGroup(atomId: number): number | null {
for (const [groupId, sgroup] of Array.from(this.sgroups)) {
if (sgroup.atoms.includes(atomId)) return groupId
}
return null
}

// TODO: simplify if bonds ids ever appear in sgroup
isBondBelongToGroup(bondId: number): number | null {
const bond = this.bonds.get(bondId)
if (!bond) return null
for (const [groupId, sgroup] of Array.from(this.sgroups)) {
if (
sgroup.atoms.includes(bond.begin) ||
sgroup.atoms.includes(bond.end)
) {
return groupId
}
}
return null
}
}
40 changes: 38 additions & 2 deletions packages/ketcher-react/src/script/editor/shared/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
***************************************************************************/

import { Vec2, fracAngle } from 'ketcher-core'
import { Vec2, fracAngle, Struct, FunctionalGroup } from 'ketcher-core'

import { inRange } from 'lodash'

Expand Down Expand Up @@ -59,10 +59,46 @@ function mergeBondsParams(struct1, bond1, struct2, bond2) {
return { merged, angle, scale, cross: Math.abs(degrees(angle)) > 90 }
}

/**
* Get all items IDs that do not belong to sgroups
* @param items {{ atoms?: number[]; bonds?: number[] } | null}
* @param struct {Struct}
* @returns {{ atoms: number[], bonds: number[] }}
*/
function getOnlyNonGroupItems(items, struct) {
const atoms =
items.atoms?.filter((key) => struct.isAtomBelongToGroup(key)) === null || []
const bonds =
items.bonds?.filter((key) => struct.isBondBelongToGroup(key)) === null || []

return { atoms, bonds }
}

/**
* Get all items IDs that do not belong to sgroups (except their attachment points)
* @param items {{ atoms?: number[]; bonds?: number[] } | null}
* @param struct {Struct}
* @returns {{ atoms: number[], bonds: number[] }}
*/
function getNonGroupItemsAndAttachmentPoints(items, struct) {
const atoms =
items.atoms?.filter(
(key) =>
struct.isAtomBelongToGroup(key) === null ||
FunctionalGroup.isAttachmentPointAtom(key, struct)
) || []
const bonds =
items.bonds?.filter((key) => !struct.isBondBelongToGroup(key)) || []

return { atoms, bonds }
}

export default {
calcAngle,
fracAngle,
calcNewAtomPos,
degrees,
mergeBondsParams
mergeBondsParams,
getOnlyNonGroupItems,
getNonGroupItemsAndAttachmentPoints
}
46 changes: 44 additions & 2 deletions packages/ketcher-react/src/script/editor/tool/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ import {
fromSimpleObjectResizing,
fromArrowResizing,
ReStruct,
ReSGroup
ReSGroup,
setExpandSGroup,
Struct,
mergeMapOfItemsToSet
} from 'ketcher-core'

import LassoHelper from './helper/lasso'
Expand Down Expand Up @@ -270,7 +273,12 @@ class SelectTool {
editor.render.page2obj(event).sub(dragCtx.xy0)
)

dragCtx.mergeItems = getItemsToFuse(editor, expSel)
const nonGroupItemsAndAttachPoints = {
...expSel,
...utils.getNonGroupItemsAndAttachmentPoints(expSel, restruct.molecule)
}

dragCtx.mergeItems = getItemsToFuse(editor, nonGroupItemsAndAttachPoints)
editor.hover(getHoverToFuse(dragCtx.mergeItems))

editor.update(dragCtx.action, true)
Expand Down Expand Up @@ -401,6 +409,15 @@ class SelectTool {
}

if (dragCtx && dragCtx.item) {
const groupsInMerge = idsOfGroupsInMerge(dragCtx.mergeItems, molecule)
if (groupsInMerge.length) {
groupsInMerge.forEach((groupId) => {
dragCtx.action.mergeWith(
setExpandSGroup(struct, groupId, { expanded: true })
)
})
}

dragCtx.action = dragCtx.action
? fromItemsFuse(struct, dragCtx.mergeItems).mergeWith(dragCtx.action)
: fromItemsFuse(struct, dragCtx.mergeItems)
Expand Down Expand Up @@ -641,6 +658,31 @@ function isSelected(selection, item) {
)
}

function idsOfGroupsInMerge(
mergeMaps: {
atoms?: Map<number, number>
bonds?: Map<number, number>
} | null,
struct: Struct
): number[] {
const groupsIds: number[] = []

const atoms = mergeMaps?.atoms && mergeMapOfItemsToSet(mergeMaps.atoms)
const bonds = mergeMaps?.bonds && mergeMapOfItemsToSet(mergeMaps.bonds)

atoms?.forEach((atomId) => {
const groupId = struct.isAtomBelongToGroup(atomId)
if (groupId !== null) groupsIds.push(groupId)
})

bonds?.forEach((bondId) => {
const groupId = struct.isAtomBelongToGroup(bondId)
if (groupId !== null) groupsIds.push(groupId)
})

return groupsIds
}

function uniqArray(dest, add, reversible: boolean) {
return add.reduce((_, item) => {
if (reversible) dest = xor(dest, [item])
Expand Down

0 comments on commit e07976e

Please sign in to comment.