Skip to content

Commit

Permalink
#5400 – Expand macromolecules in micro mode
Browse files Browse the repository at this point in the history
  • Loading branch information
svvald committed Sep 25, 2024
1 parent 972c840 commit 08460af
Show file tree
Hide file tree
Showing 13 changed files with 411 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ import { fromAtomsFragmentAttr } from './atom';
import { getRelSGroupsBySelection } from './utils';
import { IMAGE_KEY, MULTITAIL_ARROW_KEY } from 'domain/constants';

export function fromMultipleMove(restruct, lists, d: Vec2) {
export function fromMultipleMove(
restruct,
lists,
d: Vec2,
shouldPerform = true,
) {
d = new Vec2(d);

const action = new Action();
Expand Down Expand Up @@ -142,7 +147,7 @@ export function fromMultipleMove(restruct, lists, d: Vec2) {
});
}

return action.perform(restruct);
return shouldPerform ? action.perform(restruct) : action;
}

export function fromStereoFlagUpdate(restruct, frid, flag = null) {
Expand Down
179 changes: 177 additions & 2 deletions packages/ketcher-core/src/application/editor/actions/sgroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,21 @@

import {
AtomAttr,
AtomMove,
SGroupAddToHierarchy,
SGroupAtomAdd,
SGroupAtomRemove,
SGroupAttr,
SGroupCreate,
SGroupDataMove,
SGroupDelete,
SGroupRemoveFromHierarchy,
} from '../operations';
import { Pile, SGroup, SGroupAttachmentPoint } from 'domain/entities';
import { Pile, SGroup, SGroupAttachmentPoint, Vec2 } from 'domain/entities';
import { atomGetAttr, atomGetDegree, atomGetSGroups } from './utils';

import { Action } from './action';
import { SgContexts } from '..';
import { Coordinates, fromMultipleMove, SgContexts } from '..';
import { uniq } from 'lodash/fp';
import { fromAtomsAttrs } from './atom';
import {
Expand Down Expand Up @@ -111,6 +113,179 @@ export function setExpandSGroup(
return action.perform(restruct);
}

export function setExpandMonomerSGroup(
restruct: Restruct,
sgid: number,
attrs: { expanded: boolean },
) {
const action = new Action();

Object.keys(attrs).forEach((key) => {
action.addOp(new SGroupAttr(sgid, key, attrs[key]));
});

const sgroup = restruct.molecule.sgroups.get(sgid);
assert(sgroup != null);

const sGroupAtoms = SGroup.getAtoms(restruct, sgroup);
const attachmentPoints = sgroup.getAttachmentPoints();
const bondsToOutside = restruct.molecule.bonds.filter((_, bond) => {
return (
(sGroupAtoms.includes(bond.begin) && !sGroupAtoms.includes(bond.end)) ||
(sGroupAtoms.includes(bond.end) && !sGroupAtoms.includes(bond.begin))
);
});

const attachmentAtomsFromOutside: number[] = [];
const attachmentAtomsFromInside: number[] = [];

for (const bond of bondsToOutside.values()) {
if (
attachmentPoints.some(
(attachmentPoint) => attachmentPoint.atomId === bond.begin,
)
) {
attachmentAtomsFromOutside.push(bond.end);
attachmentAtomsFromInside.push(bond.begin);
} else {
attachmentAtomsFromOutside.push(bond.begin);
attachmentAtomsFromInside.push(bond.begin);
}
}

const sGroupBBox = SGroup.getObjBBox(sGroupAtoms, restruct.molecule);
const sGroupWidth = sGroupBBox.p1.x - sGroupBBox.p0.x;
const sGroupHeight = sGroupBBox.p1.y - sGroupBBox.p0.y;

const visitedAtoms = new Set<number>();
const visitedSGroups = new Set<number>();

const atomsToMove = new Map<number, number[]>();
const sGroupsToMove = new Map<number, number[]>();

const prepareSubStructure = (atomId: number, subStructureKey: number) => {
if (visitedAtoms.has(atomId)) {
return;
}
visitedAtoms.add(atomId);

const atomSGroups = restruct.atoms.get(atomId)?.a.sgs;
const atomInSGroup = atomSGroups?.size && atomSGroups.size > 0;
if (atomInSGroup) {
for (const anotherSGroupId of atomSGroups.values()) {
if (visitedSGroups.has(anotherSGroupId) || anotherSGroupId === sgid) {
continue;
}
visitedSGroups.add(anotherSGroupId);

const anotherSGroup = restruct.molecule.sgroups.get(anotherSGroupId);
if (!anotherSGroup) {
continue;
}

// const direction = anotherSGroup.pp.sub(sgroup.pp).normalized();
// sGroupsDirectionVectors.set(anotherSGroupId, direction);
// const moveVector = new Vec2(
// (direction.x * sGroupWidth) / 2,
// (direction.y * sGroupHeight) / 2,
// );

// action.addOp(new SGroupDataMove(anotherSGroupId, moveVector));
const previousArray = sGroupsToMove.get(subStructureKey) ?? [];
sGroupsToMove.set(
subStructureKey,
previousArray.concat(anotherSGroupId),
);
}
}

const atom = restruct.atoms.get(atomId);
if (atom) {
// let direction: Vec2;
// if (atomInSGroup) {
// const sGroupForAtom = atomSGroups.values()[0];
// direction = sGroupsDirectionVectors.get(sGroupForAtom) ?? new Vec2(0, 0);
// } else {
// direction = atom.a.pp.sub(sgroup.pp).normalized();
// }

// const direction = atom.a.pp.sub(sgroup.pp).normalized();
// const moveVector = new Vec2(
// (direction.x * sGroupHeight) / 2,
// (direction.y * sGroupWidth) / 2,
// );
// action.addOp(new AtomMove(atomId, moveVector));
// let someSGroupsAreContracted = false;
// for (const sGroupId of atom.a.sgs.values()) {
// const sGroup = restruct.molecule.sgroups.get(sGroupId);
// if (sGroup && !sGroup.isExpanded()) {
// someSGroupsAreContracted = true;
// break;
// }
// }

const previousArray = atomsToMove.get(subStructureKey) ?? [];
// if (!someSGroupsAreContracted) {
atomsToMove.set(subStructureKey, previousArray.concat(atomId));
// }

atom.a.neighbors.forEach((halfBondId) => {
const neighborAtomId =
restruct.molecule?.halfBonds?.get(halfBondId)?.end;
if (!neighborAtomId || sGroupAtoms.includes(neighborAtomId)) {
return;
}

prepareSubStructure(neighborAtomId, subStructureKey);
});
}
};

// bondsToOutside.forEach((bond) => {
// const direction = atom.a.pp.sub(sgroup.pp).normalized();
// });
attachmentAtomsFromOutside.forEach((atomId, index) => {
prepareSubStructure(atomId, index);
});

atomsToMove.forEach((atomIds, key) => {
const sGroups = sGroupsToMove.get(key) ?? [];
const subStructBBox = SGroup.getObjBBox(atomIds, restruct.molecule);
// const subStructWidth = subStructBBox.p1.x - subStructBBox.p0.x;
// const subStructHeight = subStructBBox.p1.y - subStructBBox.p0.y;
const subStructCenter = new Vec2(
subStructBBox.p0.x + subStructBBox.p1.x / 2,
subStructBBox.p0.y + subStructBBox.p1.y / 2,
);
const sGroupCenter = new Vec2(
sGroupBBox.p0.x + sGroupBBox.p1.x / 2,
sGroupBBox.p0.y + sGroupBBox.p1.y / 2,
);
const direction = subStructCenter.sub(sGroupCenter).normalized();
const moveVector = new Vec2(
(direction.x * sGroupHeight) / 2,
(direction.y * sGroupWidth) / 2,
);

atomIds.forEach((atomId) => {
action.addOp(new AtomMove(atomId, moveVector));
});
sGroups.forEach((sGroupId) => {
action.addOp(new SGroupDataMove(sGroupId, moveVector));
});
});

sGroupAtoms
.filter((aid) => !attachmentAtomsFromInside.includes(aid))
.forEach((aid) => {
action.mergeWith(
fromAtomsAttrs(restruct, aid, restruct.atoms.get(aid)?.a, false),
);
});

return action.perform(restruct);
}

// todo delete after supporting expand - collapse for 2 attachment points
export function expandSGroupWithMultipleAttachmentPoint(restruct) {
const action = new Action();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,10 @@ class ReAtom extends ReObject {
return;
}

if (sgroup instanceof MonomerMicromolecule && atom.rglabel) {
return;
}

if (
FunctionalGroup.isAtomInContractedFunctionalGroup(
atom,
Expand Down
55 changes: 41 additions & 14 deletions packages/ketcher-core/src/application/render/restruct/rebond.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
***************************************************************************/

import {
AmbiguousMonomer,
Atom,
Bond,
FunctionalGroup,
Expand Down Expand Up @@ -65,10 +64,7 @@ class ReBond extends ReObject {
atomId: number,
sgroup?: SGroup,
) {
return sgroup instanceof MonomerMicromolecule &&
!(sgroup.monomer instanceof AmbiguousMonomer)
? (sgroup.getAttachmentAtomId() as number)
: sgroup?.isContracted()
return sgroup?.isContracted()
? sgroup?.getContractedPosition(struct).atomId
: atomId;
}
Expand All @@ -92,15 +88,34 @@ class ReBond extends ReObject {
) {
return;
}
const p1 =
sgroup1 instanceof MonomerMicromolecule
? (sgroup1.pp as Vec2)
: beginAtom.a.pp;

const p2 =
sgroup2 instanceof MonomerMicromolecule
? (sgroup2.pp as Vec2)
: endAtom.a.pp;

let p1: Vec2;
let p2: Vec2;

if (sgroup1 instanceof MonomerMicromolecule && sgroup1 !== sgroup2) {
p1 = sgroup1.isContracted() ? (sgroup1.pp as Vec2) : beginAtom.a.pp;
} else {
p1 = beginAtom.a.pp;
}

if (sgroup2 instanceof MonomerMicromolecule && sgroup1 !== sgroup2) {
p2 = sgroup2.isContracted() ? (sgroup2.pp as Vec2) : endAtom.a.pp;
} else {
p2 = endAtom.a.pp;
}

// if (
// sgroup1 instanceof MonomerMicromolecule &&
// sgroup2 instanceof MonomerMicromolecule &&
// sgroup1 !== sgroup2
// ) {
// p1 = sgroup1.isContracted() ? (sgroup1.pp as Vec2) : beginAtom.a.pp;
// p2 = sgroup2.isContracted() ? (sgroup2.pp as Vec2) : endAtom.a.pp;
// } else {
// p1 = beginAtom.a.pp;
// p2 = endAtom.a.pp;
// }

const hb1 = restruct.molecule.halfBonds.get(bond.b.hb1);
const hb2 = restruct.molecule.halfBonds.get(bond.b.hb2);

Expand Down Expand Up @@ -317,6 +332,18 @@ class ReBond extends ReObject {
return;
}

const isSomeSGroupMonomer = sgroups.some((sGroup) => sGroup instanceof MonomerMicromolecule);
// Do not render bonds leading to leavingAtoms
const bondForLeavingGroup = sgroups.some((sGroup) => {
return sGroup.getAttachmentPoints().some((attachmentPoint) => {
return attachmentPoint.leaveAtomId === bond.begin || attachmentPoint.leaveAtomId === bond.end;
});
});

if (isSomeSGroupMonomer && bondForLeavingGroup) {
return;
}

if (
bond &&
FunctionalGroup.isBondInContractedFunctionalGroup(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
SGroup,
Vec2,
Struct,
MonomerMicromolecule,
} from 'domain/entities';
import { SgContexts } from 'application/editor/shared/constants';
import ReDataSGroupData from './redatasgroupdata';
Expand Down Expand Up @@ -129,7 +130,8 @@ class ReSGroup extends ReObject {
];
if (
sgroupTypesWithBrackets.includes(sgroup.type) &&
!sgroup.isSuperatomWithoutLabel
!sgroup.isSuperatomWithoutLabel &&
!(sgroup instanceof MonomerMicromolecule)
) {
SGroupdrawBrackets(SGroupdrawBracketsOptions);
}
Expand Down Expand Up @@ -232,9 +234,25 @@ class ReSGroup extends ReObject {
set.push(sGroupItem.hovering);

SGroup.getAtoms(render.ctab.molecule, sGroupItem).forEach((aid) => {
const atom = render.ctab.atoms.get(aid);
if (this.item instanceof MonomerMicromolecule && atom?.a.rglabel) {
return;
}
set.push(render?.ctab?.atoms?.get(aid)?.makeHoverPlate(render));
}, this);
SGroup.getBonds(render.ctab.molecule, sGroupItem).forEach((bid) => {
const bond = render.ctab.bonds.get(bid);
const apBond = this.item
?.getAttachmentPoints()
.some((attachmentPoint) => {
return (
attachmentPoint.leaveAtomId === bond?.b.begin ||
attachmentPoint.leaveAtomId === bond?.b.end
);
});
if (this.item instanceof MonomerMicromolecule && apBond) {
return;
}
set.push(render?.ctab?.bonds?.get(bid)?.makeHoverPlate(render));
}, this);
render.ctab.addReObjectPath(LayerMap.hovering, this.visel, set);
Expand Down
Loading

0 comments on commit 08460af

Please sign in to comment.