in packages/ketcher-core/src/application/editor/actions/bond.ts [50:213]
export function fromBondAddition(
reStruct: ReStruct,
bond: Partial<BondAttributes>,
begin: number | AtomAttributes,
end: number | AtomAttributes,
beginAtomPos?: Vec2,
endAtomPos?: Vec2,
): [Action, number, number, number] {
const action = new Action();
const struct = reStruct.molecule;
const mouseDownNothingAndUpNothing = (
beginAtomAttr: AtomAttributes,
endAtomAttr: AtomAttributes,
) => {
const newFragmentId = (
action.addOp(new FragmentAdd().perform(reStruct)) as FragmentAdd
).frid;
const newBeginAtomId: number = (
action.addOp(
new AtomAdd(
{ ...beginAtomAttr, fragment: newFragmentId },
beginAtomPos,
).perform(reStruct),
) as AtomAdd
).data.aid;
const newEndAtomId: number = (
action.addOp(
new AtomAdd(
{ ...endAtomAttr, fragment: newFragmentId },
endAtomPos,
).perform(reStruct),
) as AtomAdd
).data.aid;
return [newBeginAtomId, newEndAtomId] as const;
};
const mouseDownNothingAndUpAtom = (
beginAtomAttr: AtomAttributes,
endAtomId: number,
) => {
const fragmentId = atomGetAttr(reStruct, endAtomId, 'fragment');
const newBeginAtomId: number = (
action.addOp(
new AtomAdd(
{ ...beginAtomAttr, fragment: fragmentId },
beginAtomPos,
).perform(reStruct),
) as AtomAdd
).data.aid;
const endAtom = struct.atoms.get(endAtomId);
if (
endAtom &&
!FunctionalGroup.isAtomInContractedFunctionalGroup(
endAtom,
struct.sgroups,
struct.functionalGroups,
false,
)
) {
mergeSgroups(action, reStruct, [newBeginAtomId], endAtomId);
}
return [newBeginAtomId, endAtomId] as const;
};
const mouseDownAtomAndUpNothing = (
beginAtomId: number,
endAtomAttr: AtomAttributes,
) => {
const fragmentId = atomGetAttr(reStruct, beginAtomId, 'fragment');
const newEndAtomId: number = (
action.addOp(
new AtomAdd(
{
...endAtomAttr,
fragment: fragmentId,
},
endAtomPos ?? atomForNewBond(reStruct, begin, bond).pos,
).perform(reStruct),
) as AtomAdd
).data.aid;
const beginAtom = struct.atoms.get(beginAtomId);
if (
beginAtom &&
!FunctionalGroup.isAtomInContractedFunctionalGroup(
beginAtom,
struct.sgroups,
struct.functionalGroups,
false,
)
) {
mergeSgroups(action, reStruct, [newEndAtomId], beginAtomId);
}
return [beginAtomId, newEndAtomId] as const;
};
let beginAtomId: number, endAtomId: number;
const startsOnAtom = typeof begin === 'number';
const endsOnAtom = typeof end === 'number';
if (!startsOnAtom && !endsOnAtom) {
[beginAtomId, endAtomId] = mouseDownNothingAndUpNothing(begin, end);
} else if (!startsOnAtom && endsOnAtom) {
[beginAtomId, endAtomId] = mouseDownNothingAndUpAtom(begin, end);
} else if (startsOnAtom && !endsOnAtom) {
[beginAtomId, endAtomId] = mouseDownAtomAndUpNothing(begin, end);
} else {
[beginAtomId, endAtomId] = [begin as number, end as number];
if (reStruct.sgroups && reStruct.sgroups.size > 0) {
reStruct.sgroups.forEach((sgroup) => {
if (sgroup.item?.type && sgroup.item?.type === 'SUP') {
addAttachmentPointToSuperatom(sgroup, beginAtomId, endAtomId);
}
});
}
}
if (atomGetAttr(reStruct, beginAtomId, 'label') === '*') {
action.addOp(new AtomAttr(beginAtomId, 'label', 'C').perform(reStruct));
}
if (atomGetAttr(reStruct, endAtomId, 'label') === '*') {
action.addOp(new AtomAttr(endAtomId, 'label', 'C').perform(reStruct));
}
const newBondId = (
action.addOp(
new BondAdd(beginAtomId, endAtomId, bond).perform(reStruct),
) as BondAdd
).data.bid;
const newBond = struct.bonds.get(newBondId);
if (newBond) {
action.addOp(
new CalcImplicitH([newBond.begin, newBond.end]).perform(reStruct),
);
action.mergeWith(fromBondStereoUpdate(reStruct, newBond));
}
action.operations.reverse();
const mergedFragmentId = mergeFragmentsIfNeeded(
action,
reStruct,
beginAtomId,
endAtomId,
);
if (struct.frags.get(mergedFragmentId || 0)?.stereoAtoms && !bond.stereo) {
action.addOp(
new FragmentStereoFlag(mergedFragmentId || 0).perform(reStruct),
);
}
return [action, beginAtomId, endAtomId, newBondId];
}