parsers_utils_assignBonds.ts
import { AtomSpec } from "specs";
import { areConnected } from "./areConnected";
import { ParserOptionsSpec } from "parsers/ParserOptionsSpec";
/**
* @param {AtomSpec[]} atoms
*/
const OFFSETS = [
{ x: 0, y: 0, z: 1 },
{ x: 0, y: 1, z: -1 },
{ x: 0, y: 1, z: 0 },
{ x: 0, y: 1, z: 1 },
{ x: 1, y: -1, z: -1 },
{ x: 1, y: -1, z: 0 },
{ x: 1, y: -1, z: 1 },
{ x: 1, y: 0, z: -1 },
{ x: 1, y: 0, z: 0 },
{ x: 1, y: 0, z: 1 },
{ x: 1, y: 1, z: -1 },
{ x: 1, y: 1, z: 0 },
{ x: 1, y: 1, z: 1 },
];
const MAX_BOND_LENGTH = 4.95; // (largest bond length, Cs) 2.25 * 2 * 1.1 (fudge factor)
export function assignBonds(atoms: AtomSpec[], options: ParserOptionsSpec) {
// Assign bonds - yuck, can't count on connect records
for (let i = 0, n = atoms.length; i < n; i++) {
// Don't reindex if atoms are already indexed
if (!atoms[i].index) atoms[i].index = i;
}
const grid: {
x: {
y: {
z: AtomSpec[];
};
};
} = {
x: {
y: {
z: [],
},
},
};
for (let index = 0; index < atoms.length; index++) {
const atom = atoms[index];
const x = Math.floor(atom.x / MAX_BOND_LENGTH);
const y = Math.floor(atom.y / MAX_BOND_LENGTH);
const z = Math.floor(atom.z / MAX_BOND_LENGTH);
if (!grid[x]) {
grid[x] = {};
}
if (!grid[x][y]) {
grid[x][y] = {};
}
if (!grid[x][y][z]) {
grid[x][y][z] = [];
}
grid[x][y][z].push(atom);
}
function findConnections(
points: Array<AtomSpec>,
otherPoints: Array<AtomSpec>
) {
for (let i = 0; i < points.length; i++) {
const atom1 = points[i];
for (let j = 0; j < otherPoints.length; j++) {
const atom2 = otherPoints[j];
if (areConnected(atom1, atom2, options)) {
//gracefully handle one-sided bonds
const a2i = atom1.bonds.indexOf(atom2.index);
const a1i = atom2.bonds.indexOf(atom1.index);
if (a2i === -1 && a1i === -1) {
atom1.bonds.push(atom2.index);
atom1.bondOrder.push(1);
atom2.bonds.push(atom1.index);
atom2.bondOrder.push(1);
} else if (a2i === -1) {
atom1.bonds.push(atom2.index);
atom1.bondOrder.push(atom2.bondOrder[a1i]);
} else if (a1i === -1) {
atom2.bonds.push(atom1.index);
atom2.bondOrder.push(atom1.bondOrder[a2i]);
}
}
}
}
}
for (let xg in grid) {
const x = parseInt(xg);
for (let yg in grid[x]) {
const y = parseInt(yg);
for (let zg in grid[x][y]) {
const z = parseInt(zg);
const points = grid[x][y][z];
for (let i = 0; i < points.length; i++) {
const atom1 = points[i];
for (let j = i + 1; j < points.length; j++) {
const atom2 = points[j];
if (areConnected(atom1, atom2,options)) {
if (atom1.bonds.indexOf(atom2.index) == -1) {
atom1.bonds.push(atom2.index);
atom1.bondOrder.push(1);
atom2.bonds.push(atom1.index);
atom2.bondOrder.push(1);
}
}
}
}
for (let o = 0; o < OFFSETS.length; o++) {
const offset = OFFSETS[o];
if (
!grid[x + offset.x] ||
!grid[x + offset.x][y + offset.y] ||
!grid[x + offset.x][y + offset.y][z + offset.z]
)
continue;
const otherPoints = grid[x + offset.x][y + offset.y][z + offset.z];
findConnections(points, otherPoints);
}
}
}
}
}