molstar
Version:
A comprehensive macromolecular library.
262 lines (261 loc) • 9.07 kB
JavaScript
"use strict";
/**
* Copyright (c) 2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.JSONCifLigandGraph = void 0;
const linear_algebra_1 = require("../../mol-math/linear-algebra");
const mol_util_1 = require("../../mol-util");
const map_1 = require("../../mol-util/map");
const _State = {
p1: (0, linear_algebra_1.Vec3)(),
p2: (0, linear_algebra_1.Vec3)(),
};
class JSONCifLigandGraph {
getAtomAtIndex(index) {
return this.atoms[index];
}
getAtom(atomOrId) {
return typeof atomOrId === 'number' ? this.atomsById.get(atomOrId) : atomOrId;
}
getBonds(atomOrId) {
var _a;
const atom = this.getAtom(atomOrId);
if (!atom)
return [];
return (_a = this.bondByKey.get(atom.key)) !== null && _a !== void 0 ? _a : [];
}
getAtomCoords(atomOrId, out = (0, linear_algebra_1.Vec3)()) {
const atom = this.getAtom(atomOrId);
if (!atom)
return out;
const { Cartn_x, Cartn_y, Cartn_z } = atom.row;
return linear_algebra_1.Vec3.set(out, Cartn_x, Cartn_y, Cartn_z);
}
getBondDirection(bond, out = (0, linear_algebra_1.Vec3)()) {
const a1 = this.getAtomCoords(bond.atom_1, _State.p1);
const a2 = this.getAtomCoords(bond.atom_2, _State.p2);
const dir = linear_algebra_1.Vec3.sub(out, a2, a1);
return dir;
}
modifyAtom(atomOrId, data) {
const atom = this.getAtom(atomOrId);
if (!atom)
return;
atom.row = { ...atom.row, ...data, id: atom.row.id };
}
addAtom(data) {
const atom = {
key: mol_util_1.UUID.create22(),
final_id: undefined,
row: { ...data, id: undefined },
};
this.atomsByKey.set(atom.key, atom);
this.atoms.push(atom);
return atom;
}
removeAtom(atomOrId) {
const atom = this.getAtom(atomOrId);
if (!atom)
return;
if (typeof atom.row.id === 'number') {
this.removedAtomIds.add(atom.row.id);
this.atomsById.delete(atom.row.id);
}
this.atoms.splice(this.atoms.indexOf(atom), 1);
this.atomsByKey.delete(atom.key);
const bonds = this.bondByKey.get(atom.key);
if (!bonds)
return;
this.bondByKey.delete(atom.key);
for (const b of bonds) {
const bBonds = this.bondByKey.get(b.atom_2.key);
if (!bBonds)
continue;
this.bondByKey.set(b.atom_2.key, bBonds.filter(bb => bb.atom_2 !== atom));
}
}
addOrUpdateBond(atom1, atom2, props) {
const a1 = this.getAtom(atom1);
const a2 = this.getAtom(atom2);
if (!a1 || !a2)
return;
const ps = { ...props };
this.removeBond(atom1, atom2);
(0, map_1.arrayMapAdd)(this.bondByKey, a1.key, { atom_1: a1, atom_2: a2, props: ps });
(0, map_1.arrayMapAdd)(this.bondByKey, a2.key, { atom_1: a2, atom_2: a1, props: ps });
}
removeBond(atom1, atom2) {
const a1 = this.getAtom(atom1);
const a2 = this.getAtom(atom2);
if (!a1 || !a2)
return;
const a1Bonds = this.bondByKey.get(a1.key);
if (a1Bonds) {
this.bondByKey.set(a1.key, a1Bonds.filter(b => b.atom_2 !== a2));
}
const a2Bonds = this.bondByKey.get(a2.key);
if (a2Bonds) {
this.bondByKey.set(a2.key, a2Bonds.filter(b => b.atom_2 !== a1));
}
}
transformAtomCoords(xform, atomOrId) {
const atom = this.getAtom(atomOrId);
if (!atom)
return;
const p = this.getAtomCoords(atom, _State.p1);
linear_algebra_1.Vec3.transformMat4(p, p, xform);
atom.row.Cartn_x = p[0];
atom.row.Cartn_y = p[1];
atom.row.Cartn_z = p[2];
}
transformCoords(xform, atoms) {
for (const a of atoms !== null && atoms !== void 0 ? atoms : this.atoms) {
this.transformAtomCoords(xform, a);
}
}
traverse(atomOrId, how, state, visitAtom) {
const start = this.getAtom(atomOrId);
if (!start)
return state;
const visited = new Set();
const pred = new Map();
const queue = [start.key];
while (queue.length) {
const key = how === 'bfs' ? queue.shift() : queue.pop();
if (visited.has(key))
continue;
const a = this.atomsByKey.get(key);
visited.add(a.key);
visitAtom(a, state, pred.get(key), this);
const bs = this.bondByKey.get(a.key);
if (!(bs === null || bs === void 0 ? void 0 : bs.length))
continue;
for (const b of bs) {
if (visited.has(b.atom_2.key))
continue;
queue.push(b.atom_2.key);
pred.set(b.atom_2.key, b);
}
}
return state;
}
getData() {
const atomIdRemapping = new Map();
const addedAtomIds = [];
const sortedAtoms = this.atoms.map((a, i) => [a, i]);
sortedAtoms.sort((a, b) => {
const x = a[0].row.type_symbol;
const y = b[0].row.type_symbol;
if (x === 'H' && y !== 'H')
return 1;
if (x !== 'H' && y === 'H')
return -1;
return a[1] - b[1];
});
const atoms = [];
for (let i = 0; i < sortedAtoms.length; ++i) {
const a = sortedAtoms[i][0];
const id = i + 1;
if (a.row.id === undefined) {
addedAtomIds.push(id);
}
else {
atomIdRemapping.set(a.row.id, id);
}
a.final_id = id;
atoms.push({ ...a.row, id });
}
const block = {
...this.data,
categories: {
...this.data.categories,
atom_site: {
...this.data.categories['atom_site'],
rows: atoms,
},
}
};
const bonds = [];
for (const [a] of sortedAtoms) {
const xs = this.bondByKey.get(a.key);
if (!xs)
continue;
for (const bb of xs) {
if (a.final_id >= bb.atom_2.final_id)
continue;
bonds.push({
atom_id_1: a.final_id,
atom_id_2: bb.atom_2.final_id,
value_order: bb.props.value_order,
type_id: bb.props.type_id,
});
}
}
bonds.sort((a, b) => {
if (a.atom_id_1 !== b.atom_id_1)
return a.atom_id_1 - b.atom_id_1;
if (a.atom_id_2 !== b.atom_id_2)
return a.atom_id_2 - b.atom_id_2;
return 0;
});
if (block.categories.molstar_bond_site) {
block.categories['molstar_bond_site'] = {
...block.categories['molstar_bond_site'],
rows: bonds
};
}
return {
block,
atomIdRemapping,
addedAtomIds,
removedAtomIds: Array.from(this.removedAtomIds).sort((a, b) => a - b),
};
}
constructor(data) {
this.data = data;
this.atoms = [];
this.atomsByKey = new Map();
this.atomsById = new Map();
/** Bond with the provided key is always atom_1 */
this.bondByKey = new Map();
this.removedAtomIds = new Set();
for (const row of data.categories['atom_site'].rows) {
const atom = {
key: mol_util_1.UUID.create22(),
final_id: row.final_id,
row: { ...row },
};
this.atoms.push(atom);
this.atomsByKey.set(atom.key, atom);
this.atomsById.set(row.id, atom);
}
if (!data.categories.molstar_bond_site)
return;
for (const row of data.categories.molstar_bond_site.rows) {
const atom_1 = this.atomsById.get(row.atom_id_1);
const atom_2 = this.atomsById.get(row.atom_id_2);
if (!atom_1 || !atom_2)
continue;
(0, map_1.arrayMapAdd)(this.bondByKey, atom_1.key, {
atom_1: atom_1,
atom_2: atom_2,
props: {
value_order: row.value_order,
type_id: row.type_id,
},
});
(0, map_1.arrayMapAdd)(this.bondByKey, atom_2.key, {
atom_1: atom_2,
atom_2: atom_1,
props: {
value_order: row.value_order,
type_id: row.type_id,
}
});
}
}
}
exports.JSONCifLigandGraph = JSONCifLigandGraph;