in packages/ketcher-core/src/application/editor/actions/utils.ts [107:224]
export function atomForNewBond(restruct, id, bond?) {
// eslint-disable-line max-statements
const neighbours: Array<{ id: number; v: Vec2 }> = [];
const pos = atomGetPos(restruct, id);
const atomNeighbours = restruct.molecule.atomGetNeighbors(id);
const prevBondId = restruct.molecule.findBondId(
id,
atomNeighbours.length ? atomNeighbours[0]?.aid : undefined,
);
const prevBond = restruct.molecule.bonds.get(prevBondId);
const prevBondType = prevBond ? prevBond.type : bond ? bond.type : 1;
restruct.molecule.atomGetNeighbors(id).forEach((nei) => {
const neiPos = atomGetPos(restruct, nei.aid);
if (Vec2.dist(pos, neiPos) < 0.1) return;
neighbours.push({ id: nei.aid, v: Vec2.diff(neiPos, pos) });
});
neighbours.sort(
(nei1, nei2) =>
Math.atan2(nei1.v.y, nei1.v.x) - Math.atan2(nei2.v.y, nei2.v.x),
);
let i;
let maxI = 0;
let angle;
let maxAngle = 0;
// TODO: impove layout: tree, ...
for (i = 0; i < neighbours.length; i++) {
angle = Vec2.angle(
neighbours[i].v,
neighbours[(i + 1) % neighbours.length].v,
);
if (angle < 0) angle += 2 * Math.PI;
if (angle > maxAngle) {
maxI = i;
maxAngle = angle;
}
}
let v = new Vec2(1, 0);
if (neighbours.length > 0) {
if (neighbours.length === 1) {
maxAngle = -((4 * Math.PI) / 3);
// zig-zag
const nei = restruct.molecule.atomGetNeighbors(id)[0];
if (atomGetDegree(restruct, nei.aid) > 1) {
const neiNeighbours: Array<any> = [];
const neiPos = atomGetPos(restruct, nei.aid);
const neiV = Vec2.diff(pos, neiPos);
const neiAngle = Math.atan2(neiV.y, neiV.x);
restruct.molecule.atomGetNeighbors(nei.aid).forEach((neiNei) => {
const neiNeiPos = atomGetPos(restruct, neiNei.aid);
if (neiNei.bid === nei.bid || Vec2.dist(neiPos, neiNeiPos) < 0.1) {
return;
}
const vDiff = Vec2.diff(neiNeiPos, neiPos);
let ang = Math.atan2(vDiff.y, vDiff.x) - neiAngle;
if (ang < 0) ang += 2 * Math.PI;
neiNeighbours.push(ang);
});
neiNeighbours.sort((nei1, nei2) => nei1 - nei2);
if (
neiNeighbours[0] <= Math.PI * 1.01 &&
neiNeighbours[neiNeighbours.length - 1] <= 1.01 * Math.PI
) {
maxAngle *= -1;
}
}
}
const shallBe180DegToPrevBond =
(neighbours.length === 1 &&
prevBondType === bond?.type &&
(bond?.type === Bond.PATTERN.TYPE.DOUBLE ||
bond?.type === Bond.PATTERN.TYPE.TRIPLE)) ||
(prevBondType === Bond.PATTERN.TYPE.SINGLE &&
bond?.type === Bond.PATTERN.TYPE.TRIPLE) ||
(prevBondType === Bond.PATTERN.TYPE.TRIPLE &&
bond?.type === Bond.PATTERN.TYPE.SINGLE);
if (shallBe180DegToPrevBond) {
const prevBondAngle = restruct.molecule.bonds.get(prevBondId).angle;
if (prevBondAngle > -90 && prevBondAngle < 90 && neighbours[0].v.x > 0) {
angle = (prevBondAngle * Math.PI) / 180 + Math.PI;
} else {
angle = (prevBondAngle * Math.PI) / 180;
}
} else {
angle =
maxAngle / 2 + Math.atan2(neighbours[maxI].v.y, neighbours[maxI].v.x);
}
v = v.rotate(angle);
}
v.add_(pos); // eslint-disable-line no-underscore-dangle
let a: any = closest.atom(restruct, v, null, 0.1);
a = a === null ? { label: 'C' } : a.id;
return { atom: a, pos: v };
}