diff --git a/src/parsers/MMTF.ts b/src/parsers/MMTF.ts index 27509d73d..b775498ee 100644 --- a/src/parsers/MMTF.ts +++ b/src/parsers/MMTF.ts @@ -47,7 +47,6 @@ let mmtfHETATMtypes = new Set([ * @param ParserOptionsSpec * @category Parsers */ - export function MMTFparser(bindata: any, options: ParserOptionsSpec) { var noH = !options.keepH; // suppress hydrogens by default @@ -300,7 +299,7 @@ export function MMTFparser(bindata: any, options: ParserOptionsSpec) { } if (computeStruct && !ignoreStruct) { - computeSecondaryStructure(atoms, options.hbondCutoff); + computeSecondaryStructure(atoms as any, options.hbondCutoff); } return atoms; diff --git a/src/parsers/utils/anumToSymbol.ts b/src/parsers/utils/anumToSymbol.ts index 62334469b..46f394431 100644 --- a/src/parsers/utils/anumToSymbol.ts +++ b/src/parsers/utils/anumToSymbol.ts @@ -1,4 +1,4 @@ -export const anumToSymbol:any = { +export const anumToSymbol:Record = { 1: 'H', 2: 'He', 3:'Li',4:'Be', 5: 'B', 6: 'C', 7:'N', 8:'O', 9:'F', 10: 'Ne', 11: 'Na',12:'Mg', 13: 'Al',14:'Si',15:'P',16:'S',17:'Cl',18:'Ar', diff --git a/src/parsers/utils/areConnected.ts b/src/parsers/utils/areConnected.ts index 923c3cd5e..f456ab18b 100644 --- a/src/parsers/utils/areConnected.ts +++ b/src/parsers/utils/areConnected.ts @@ -1,34 +1,34 @@ +import { AtomSpec } from "specs"; import { bondLength } from "./bondLength"; /* * Return true if atom1 and atom2 are probably bonded to each other based on distance alone -*/ - -export function areConnected(atom1: { elem: any; x: number; y: number; z: number; altLoc: string; }, atom2: { elem: any; x: number; y: number; z: number; altLoc: string; }) { - var maxsq = bondLength(atom1.elem) + bondLength(atom2.elem); + */ +export function areConnected(atom1: AtomSpec, atom2: AtomSpec) { + let maxsq = bondLength(atom1.elem) + bondLength(atom2.elem); maxsq += 0.25; // fudge factor, especially important for md frames, also see 1i3d maxsq *= maxsq; - var xdiff = atom1.x - atom2.x; + let xdiff = atom1.x - atom2.x; xdiff *= xdiff; if (xdiff > maxsq) return false; - var ydiff = atom1.y - atom2.y; + let ydiff = atom1.y - atom2.y; ydiff *= ydiff; if (ydiff > maxsq) return false; - var zdiff = atom1.z - atom2.z; + let zdiff = atom1.z - atom2.z; zdiff *= zdiff; if (zdiff > maxsq) return false; - var distSquared = xdiff + ydiff + zdiff; + const distSquared = xdiff + ydiff + zdiff; - if (isNaN(distSquared)) return false; - else if (distSquared < 0.5) return false; // maybe duplicate position. - else if (distSquared > maxsq) return false; - else if ( - atom1.altLoc != atom2.altLoc && - atom1.altLoc != " " && - atom2.altLoc != " " + if ( + isNaN(distSquared) || + distSquared < 0.5 || + distSquared > maxsq || + (atom1.altLoc !== atom2.altLoc && atom1.altLoc !== " " && atom2.altLoc !== " ") ) - return false; // don't connect across alternate locations - else return true; + return false; + + + return true; } diff --git a/src/parsers/utils/assignBackboneHBonds.ts b/src/parsers/utils/assignBackboneHBonds.ts index 7531e96e8..075d01a7f 100644 --- a/src/parsers/utils/assignBackboneHBonds.ts +++ b/src/parsers/utils/assignBackboneHBonds.ts @@ -1,15 +1,21 @@ // This will identify all hydrogen bonds between backbone // atoms; assume atom names are correct, only identifies // single closest hbond -export function assignBackboneHBonds(atomsarray: string | any[], hbondCutoff: number) { - let maxlength = hbondCutoff || 3.2; - let maxlengthSq = maxlength*maxlength; - let atoms = []; + +import { AtomSpec } from "specs"; +// interface Atoms {index: number; atom: string; hbondDistanceSq: number; hbondOther: any; hetflag:any} +export function assignBackboneHBonds( + atomsarray: Array, + hbondCutoff: number +) { + const maxlength = hbondCutoff || 3.2; + const maxlengthSq = maxlength * maxlength; + const atoms = []; for (let i = 0, n = atomsarray.length; i < n; i++) { atomsarray[i].index = i; // only consider 'N' and 'O' - var atom = atomsarray[i]; + const atom = atomsarray[i]; if (!atom.hetflag && (atom.atom === "N" || atom.atom === "O")) { atoms.push(atom); atom.hbondOther = null; @@ -21,20 +27,20 @@ export function assignBackboneHBonds(atomsarray: string | any[], hbondCutoff: nu return a.z - b.z; }); for (let i = 0, n = atoms.length; i < n; i++) { - var ai = atoms[i]; + const ai = atoms[i]; for (let j = i + 1; j < n; j++) { - var aj = atoms[j]; - var zdiff = aj.z - ai.z; + const aj = atoms[j]; + const zdiff = aj.z - ai.z; if (zdiff > maxlength) // can't be connected break; if (aj.atom == ai.atom) continue; // can't be connected, but later might be - var ydiff = Math.abs(aj.y - ai.y); + const ydiff = Math.abs(aj.y - ai.y); if (ydiff > maxlength) continue; - var xdiff = Math.abs(aj.x - ai.x); + const xdiff = Math.abs(aj.x - ai.x); if (xdiff > maxlength) continue; - var dist = xdiff * xdiff + ydiff * ydiff + zdiff * zdiff; + const dist = xdiff * xdiff + ydiff * ydiff + zdiff * zdiff; if (dist > maxlengthSq) continue; if (aj.chain == ai.chain && Math.abs(aj.resi - ai.resi) < 4) continue; // ignore bonds between too close residues diff --git a/src/parsers/utils/assignBonds.ts b/src/parsers/utils/assignBonds.ts index 9485202dd..40a0488a1 100644 --- a/src/parsers/utils/assignBonds.ts +++ b/src/parsers/utils/assignBonds.ts @@ -1,25 +1,40 @@ +import { AtomSpec } from "specs"; import { areConnected } from "./areConnected"; /** * @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: string | any[]) { // Assign bonds - yuck, can't count on connect records - for (var i = 0, n = atoms.length; i < n; i++) { + 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; } - var grid = {}; - var MAX_BOND_LENGTH = 4.95; // (largest bond length, Cs) 2.25 * 2 * 1.1 (fudge factor) - - for (var index = 0; index < atoms.length; index++) { - var atom = atoms[index]; - var x = Math.floor(atom.x / MAX_BOND_LENGTH); - var y = Math.floor(atom.y / MAX_BOND_LENGTH); - var z = Math.floor(atom.z / MAX_BOND_LENGTH); + const grid = {}; + 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] = {}; } @@ -33,25 +48,28 @@ export function assignBonds(atoms: string | any[]) { grid[x][y][z].push(atom); } - var findConnections = function (points: string | any[], otherPoints: string | any[]) { - for (var i = 0; i < points.length; i++) { - var atom1 = points[i]; - for (var j = 0; j < otherPoints.length; j++) { - var atom2 = otherPoints[j]; + function findConnections( + points: Array, + otherPoints: Array + ) { + 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)) { //gracefully handle one-sided bonds - var a2i = atom1.bonds.indexOf(atom2.index); - var a1i = atom2.bonds.indexOf(atom1.index); - if (a2i == -1 && a1i == -1) { + 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) { + } else if (a2i === -1) { atom1.bonds.push(atom2.index); atom1.bondOrder.push(atom2.bondOrder[a1i]); - } else if (a1i == -1) { + } else if (a1i === -1) { atom2.bonds.push(atom1.index); atom2.bondOrder.push(atom1.bondOrder[a2i]); } @@ -60,33 +78,18 @@ export function assignBonds(atoms: string | any[]) { } }; - /*const*/ var 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 }, - ]; for (let xg in grid) { - let x = parseInt(xg); + const x = parseInt(xg); for (let yg in grid[x]) { - let y = parseInt(yg); + const y = parseInt(yg); for (let zg in grid[x][y]) { - let z = parseInt(zg); - let points = grid[x][y][z]; + const z = parseInt(zg); + const points = grid[x][y][z]; for (let i = 0; i < points.length; i++) { - let atom1 = points[i]; + const atom1 = points[i]; for (let j = i + 1; j < points.length; j++) { - let atom2 = points[j]; + const atom2 = points[j]; if (areConnected(atom1, atom2)) { if (atom1.bonds.indexOf(atom2.index) == -1) { atom1.bonds.push(atom2.index); @@ -99,7 +102,7 @@ export function assignBonds(atoms: string | any[]) { } for (let o = 0; o < OFFSETS.length; o++) { - let offset = OFFSETS[o]; + const offset = OFFSETS[o]; if ( !grid[x + offset.x] || !grid[x + offset.x][y + offset.y] || @@ -107,7 +110,7 @@ export function assignBonds(atoms: string | any[]) { ) continue; - let otherPoints = grid[x + offset.x][y + offset.y][z + offset.z]; + const otherPoints = grid[x + offset.x][y + offset.y][z + offset.z]; findConnections(points, otherPoints); } } diff --git a/src/parsers/utils/atomNameToElem.ts b/src/parsers/utils/atomNameToElem.ts index 61c33d24a..413dc370c 100644 --- a/src/parsers/utils/atomNameToElem.ts +++ b/src/parsers/utils/atomNameToElem.ts @@ -2,23 +2,31 @@ import { bondTable } from "./bondLength"; // Attempts to infer atomic element from an atom name export function atomNameToElem(name: string, nothetero: boolean) { - var elem = name.replace(/ /g, ""); - if(elem.length > 0 && elem[0] == 'H' && elem != 'Hg' && elem != 'He' && elem != 'Hf' && elem != 'Hs' && elem != 'Ho') { - elem = 'H'; //workaround weird hydrogen names from MD, note mercury must use lowercase + let elem = name.replace(/ /g, ""); + if ( + elem.length > 0 && + elem[0] === "H" && + elem !== "Hg" && + elem !== "He" && + elem !== "Hf" && + elem !== "Hs" && + elem !== "Ho" + ) { + elem = "H"; //workaround weird hydrogen names from MD, note mercury must use lowercase } - if(elem.length > 1) { - elem = elem[0].toUpperCase() + elem.substring(1).toLowerCase(); - if(typeof(bondTable[elem]) === 'undefined') { - //not a known element, probably should just use first letter - elem = elem[0]; - } else if(nothetero) { - if(elem == 'Ca') { //alpha carbon, not calcium - elem = 'C'; - } - else if(elem == 'Cd') { - elem = 'C'; - } + if (elem.length > 1) { + elem = elem[0].toUpperCase() + elem.substring(1).toLowerCase(); + if (bondTable[elem] === undefined) { + //not a known element, probably should just use first letter + elem = elem[0]; + } else if (nothetero) { + if (elem === "Ca") { + //alpha carbon, not calcium + elem = "C"; + } else if (elem === "Cd") { + elem = "C"; } + } } return elem; -}; \ No newline at end of file +} diff --git a/src/parsers/utils/bondLength.ts b/src/parsers/utils/bondLength.ts index f324966b2..a22b12943 100644 --- a/src/parsers/utils/bondLength.ts +++ b/src/parsers/utils/bondLength.ts @@ -1,6 +1,5 @@ - // Covalent radii lookup table used to identify bonds in assignBonds -export let bondTable:any = { +export let bondTable: Record = { H :0.37, He:0.32, Li:1.34,Be:0.90, B :0.82,C :0.77,N :0.75,O :0.73,F :0.71,Ne:0.69, Na:1.54,Mg:1.30, Al:1.18,Si:1.11,P :1.06,S :1.02,Cl:0.99,Ar:0.97, @@ -11,15 +10,13 @@ export let bondTable:any = { // None of the bottom row or any of the Lanthanides have bond lengths }; - // Get the length used for bond identification for the specified element. export function bondLength(elem: string | number) { return bondTable[elem] || 1.6; -}; +} // Set the length used for bond identification for the specified element. -export function setBondLength(elem:string, radius:number) { - if(radius < 0) radius = 0; +export function setBondLength(elem: string, radius: number) { + if (radius < 0) radius = 0; bondTable[elem] = radius; } - diff --git a/src/parsers/utils/computeSecondaryStructure.ts b/src/parsers/utils/computeSecondaryStructure.ts index 759280ae4..d45eecf6b 100644 --- a/src/parsers/utils/computeSecondaryStructure.ts +++ b/src/parsers/utils/computeSecondaryStructure.ts @@ -1,22 +1,23 @@ +import { AtomSpec } from "specs"; import { assignBackboneHBonds } from "./assignBackboneHBonds"; -export function computeSecondaryStructure(atomsarray: string | any[], hbondCutoff: number | undefined) { - assignBackboneHBonds(atomsarray, hbondCutoff!); +export function computeSecondaryStructure(atomsarray: Array, hbondCutoff: number) { + assignBackboneHBonds(atomsarray, hbondCutoff); // compute, per residue, what the secondary structure is - var chres = {}; // lookup by chain and resid - var i: number, il: number, c: string | number, r: number; // i: used in for loop, il: length of atomsarray - var atom: { chain: string | number; hbondDistanceSq: number; hbondOther: any; resi: number; ss: string; ssbegin: boolean; ssend: boolean; }, val: string; + const chres = {}; // lookup by chain and resid + let i: number, il: number, c: string | number, r: number; // i: used in for loop, il: length of atomsarray + let atom: AtomSpec, val: string; //identify helices first for (i = 0, il = atomsarray.length; i < il; i++) { atom = atomsarray[i]; - if (typeof chres[atom.chain] === "undefined") chres[atom.chain] = []; + if (chres[atom.chain] === undefined) chres[atom.chain] = []; if (isFinite(atom.hbondDistanceSq)) { - var other = atom.hbondOther; - if (typeof chres[other.chain] === "undefined") chres[other.chain] = []; + const other = atom.hbondOther; + if (chres[other.chain] === undefined) chres[other.chain] = []; if (Math.abs(other.resi - atom.resi) === 4) { // helix @@ -28,8 +29,8 @@ export function computeSecondaryStructure(atomsarray: string | any[], hbondCutof // plug gaps in helices for (c in chres) { for (r = 1; r < chres[c].length - 1; r++) { - var valbefore = chres[c][r - 1]; - var valafter = chres[c][r + 1]; + const valbefore = chres[c][r - 1]; + const valafter = chres[c][r + 1]; val = chres[c][r]; if (valbefore == "h" && valbefore == valafter && val != valbefore) { chres[c][r] = valbefore; @@ -44,7 +45,7 @@ export function computeSecondaryStructure(atomsarray: string | any[], hbondCutof if ( isFinite(atom.hbondDistanceSq) && chres[atom.chain][atom.resi] != "h" && - atom.ss != "h" + atom.ss !== "h" ) { chres[atom.chain][atom.resi] = "maybesheet"; } @@ -71,15 +72,15 @@ export function computeSecondaryStructure(atomsarray: string | any[], hbondCutof // plug gaps in sheets and remove singletons for (let c in chres) { for (let r = 1; r < chres[c].length - 1; r++) { - let valbefore = chres[c][r - 1]; - let valafter = chres[c][r + 1]; + const valbefore = chres[c][r - 1]; + const valafter = chres[c][r + 1]; val = chres[c][r]; if (valbefore == "s" && valbefore == valafter && val != valbefore) { chres[c][r] = valbefore; } } for (let r = 0; r < chres[c].length; r++) { - let val = chres[c][r]; + const val = chres[c][r]; if (val == "h" || val == "s") { if (chres[c][r - 1] != val && chres[c][r + 1] != val) delete chres[c][r]; @@ -95,7 +96,7 @@ export function computeSecondaryStructure(atomsarray: string | any[], hbondCutof //clear hbondOther to eliminate circular references that prohibit serialization delete atom.hbondOther; delete atom.hbondDistanceSq; - if (typeof val == "undefined" || val == "maybesheet") continue; + if (val === undefined || val === "maybesheet") continue; atom.ss = val; if (chres[atom.chain][atom.resi - 1] != val) atom.ssbegin = true; if (chres[atom.chain][atom.resi + 1] != val) atom.ssend = true; diff --git a/src/parsers/utils/validateBonds.ts b/src/parsers/utils/validateBonds.ts index 888cb9af2..17b176255 100644 --- a/src/parsers/utils/validateBonds.ts +++ b/src/parsers/utils/validateBonds.ts @@ -1,13 +1,13 @@ // Make sure bonds are actually two way export function validateBonds (atomsarray: string[] | any[], serialToIndex: number[]) { - for (var i = 0, n = atomsarray.length; i < n; i++) { - var atom = atomsarray[i]; - for(var b = 0; b < atom.bonds.length; b++) { - var a2i = atom.bonds[b]; - var atom2 = atomsarray[a2i]; - var atomi = serialToIndex[atom.serial]; + for (let i = 0, n = atomsarray.length; i < n; i++) { + const atom = atomsarray[i]; + for(let b = 0; b < atom.bonds.length; b++) { + const a2i = atom.bonds[b]; + const atom2 = atomsarray[a2i]; + const atomi = serialToIndex[atom.serial]; if(atom2 && atomi) { - var a1i = atom2.bonds.indexOf(atomi); + const a1i = atom2.bonds.indexOf(atomi); if(a1i < 0) { atom2.bonds.push(atomi); atom2.bondOrder.push(atom.bondOrder[b]); diff --git a/src/specs.ts b/src/specs.ts index e99b31d51..f8b3cdb29 100644 --- a/src/specs.ts +++ b/src/specs.ts @@ -43,6 +43,8 @@ export interface AtomSpec { bonds?: number[]; /** Secondary structure identifier (for cartoon render; e.g. 'h' for helix) */ ss?: string; + ssbegin?: boolean; + ssend?: boolean; /** true if this atom forms only single bonds or no bonds at all */ singleBonds?: boolean; /** Array of this atom's bond orders, corresponding to bonds identfied by 'bonds' */ @@ -73,6 +75,9 @@ export interface AtomSpec { capDrawn?: boolean; model?: number; contextMenuEnabled?: boolean; + hbondDistanceSq?: number; + hbondOther?: any; + altLoc?: string; }; /**