molstar
Version:
A comprehensive macromolecular library.
113 lines (112 loc) • 5.89 kB
JavaScript
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Segmentation, Interval } from '../../../../../mol-data/int';
import { SortedRanges } from '../../../../../mol-data/int/sorted-ranges';
import { isPolymer, PolymerType } from '../../types';
import { getAtomIdForAtomRole } from '../../../util';
import { Vec3 } from '../../../../../mol-math/linear-algebra';
function areBackboneConnected(riStart, riEnd, conformation, index, derived) {
const { polymerType, traceElementIndex, directionFromElementIndex, directionToElementIndex } = derived.residue;
const ptStart = polymerType[riStart];
const ptEnd = polymerType[riEnd];
if (ptStart === PolymerType.NA || ptEnd === PolymerType.NA)
return false;
if (traceElementIndex[riStart] === -1 || traceElementIndex[riEnd] === -1)
return false;
let eiStart = index.findAtomsOnResidue(riStart, getAtomIdForAtomRole(ptStart, 'backboneStart'));
let eiEnd = index.findAtomsOnResidue(riEnd, getAtomIdForAtomRole(ptEnd, 'backboneEnd'));
if (eiStart === -1 || eiEnd === -1) {
eiStart = index.findAtomsOnResidue(riStart, getAtomIdForAtomRole(ptStart, 'coarseBackbone'));
eiEnd = index.findAtomsOnResidue(riEnd, getAtomIdForAtomRole(ptEnd, 'coarseBackbone'));
}
const { x, y, z } = conformation;
const pStart = Vec3.create(x[eiStart], y[eiStart], z[eiStart]);
const pEnd = Vec3.create(x[eiEnd], y[eiEnd], z[eiEnd]);
const isCoarse = directionFromElementIndex[riStart] === -1 || directionToElementIndex[riStart] === -1 || directionFromElementIndex[riEnd] === -1 || directionToElementIndex[riEnd] === -1;
return Vec3.distance(pStart, pEnd) < (isCoarse ? 10 : 3);
}
export function getAtomicRanges(hierarchy, entities, conformation, sequence) {
const polymerRanges = [];
const gapRanges = [];
const cyclicPolymerMap = new Map();
const chainIt = Segmentation.transientSegments(hierarchy.chainAtomSegments, Interval.ofBounds(0, hierarchy.atoms._rowCount));
const residueIt = Segmentation.transientSegments(hierarchy.residueAtomSegments, Interval.ofBounds(0, hierarchy.atoms._rowCount));
const { index, derived } = hierarchy;
const { label_seq_id } = hierarchy.residues;
const { label_entity_id } = hierarchy.chains;
const { moleculeType, traceElementIndex } = derived.residue;
let prevSeqId;
let prevStart;
let prevEnd;
let startIndex;
while (chainIt.hasNext) {
const chainSegment = chainIt.move();
residueIt.setSegment(chainSegment);
prevSeqId = -1;
prevStart = -1;
prevEnd = -1;
startIndex = -1;
const eI = entities.getEntityIndex(label_entity_id.value(chainSegment.index));
const seq = sequence.byEntityKey[eI];
const maxSeqId = seq ? seq.sequence.seqId.value(seq.sequence.seqId.rowCount - 1) : -1;
// check cyclic peptides, seqIds and distance must be compatible
const riStart = hierarchy.residueAtomSegments.index[chainSegment.start];
const riEnd = hierarchy.residueAtomSegments.index[chainSegment.end - 1];
const seqIdStart = label_seq_id.value(riStart);
const seqIdEnd = label_seq_id.value(riEnd);
if (seqIdStart === 1 && seqIdEnd === maxSeqId && conformation.xyzDefined && areBackboneConnected(riStart, riEnd, conformation, index, derived)) {
cyclicPolymerMap.set(riStart, riEnd);
cyclicPolymerMap.set(riEnd, riStart);
}
while (residueIt.hasNext) {
const residueSegment = residueIt.move();
const residueIndex = residueSegment.index;
const seqId = label_seq_id.value(residueIndex);
// treat polymers residues that don't have a trace element resolved as gaps
if (isPolymer(moleculeType[residueIndex]) && traceElementIndex[residueIndex] !== -1) {
if (startIndex !== -1) {
if (seqId !== prevSeqId + 1) {
polymerRanges.push(startIndex, prevEnd - 1);
gapRanges.push(prevStart, residueSegment.end - 1);
startIndex = residueSegment.start;
}
else if (!residueIt.hasNext) {
polymerRanges.push(startIndex, residueSegment.end - 1);
// TODO store terminal gaps
}
else {
const riStart = hierarchy.residueAtomSegments.index[residueSegment.start];
const riEnd = hierarchy.residueAtomSegments.index[prevEnd - 1];
if (conformation.xyzDefined && !areBackboneConnected(riStart, riEnd, conformation, hierarchy.index, derived)) {
polymerRanges.push(startIndex, prevEnd - 1);
// add gap even for consecutive residues if they are not connected
gapRanges.push(prevStart, residueSegment.end - 1);
startIndex = residueSegment.start;
}
}
}
else {
startIndex = residueSegment.start; // start polymer
// TODO store terminal gaps
}
}
else {
if (startIndex !== -1) {
polymerRanges.push(startIndex, prevEnd - 1);
startIndex = -1;
}
}
prevStart = residueSegment.start;
prevEnd = residueSegment.end;
prevSeqId = seqId;
}
}
return {
polymerRanges: SortedRanges.ofSortedRanges(polymerRanges),
gapRanges: SortedRanges.ofSortedRanges(gapRanges),
cyclicPolymerMap
};
}