Skip to content
Snippets Groups Projects
Commit 40c82387 authored by Alexander Rose's avatar Alexander Rose
Browse files

added support for cyclic atomic polymers

parent cad35f7c
Branches
Tags
No related merge requests found
...@@ -25,8 +25,7 @@ import { getElementLoci, markElement } from './util/element'; ...@@ -25,8 +25,7 @@ import { getElementLoci, markElement } from './util/element';
import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import { Vec3, Mat4 } from 'mol-math/linear-algebra';
import { Segmentation, SortedArray } from 'mol-data/int'; import { Segmentation, SortedArray } from 'mol-data/int';
import { MoleculeType, isNucleic, isPurinBase, isPyrimidineBase } from 'mol-model/structure/model/types'; import { MoleculeType, isNucleic, isPurinBase, isPyrimidineBase } from 'mol-model/structure/model/types';
import { getElementIndexForAtomId } from 'mol-model/structure/util'; import { getElementIndexForAtomId, getElementIndexForAtomRole } from 'mol-model/structure/util';
import { getElementIndexForResidueTypeAtomId } from './util/polymer';
async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, mesh?: Mesh) { async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, mesh?: Mesh) {
if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh) if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
...@@ -77,7 +76,7 @@ async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, mesh?: ...@@ -77,7 +76,7 @@ async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, mesh?:
idx3 = getElementIndexForAtomId(model, residueIndex, 'C6') idx3 = getElementIndexForAtomId(model, residueIndex, 'C6')
idx4 = getElementIndexForAtomId(model, residueIndex, 'C2') idx4 = getElementIndexForAtomId(model, residueIndex, 'C2')
idx5 = getElementIndexForAtomId(model, residueIndex, 'N9') idx5 = getElementIndexForAtomId(model, residueIndex, 'N9')
idx6 = getElementIndexForResidueTypeAtomId(model, residueIndex, 'trace') idx6 = getElementIndexForAtomRole(model, residueIndex, 'trace')
} else if (isPyrimidineBase(compId)) { } else if (isPyrimidineBase(compId)) {
height = 3.0 height = 3.0
idx1 = getElementIndexForAtomId(model, residueIndex, 'N3') idx1 = getElementIndexForAtomId(model, residueIndex, 'N3')
...@@ -85,7 +84,7 @@ async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, mesh?: ...@@ -85,7 +84,7 @@ async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, mesh?:
idx3 = getElementIndexForAtomId(model, residueIndex, 'C4') idx3 = getElementIndexForAtomId(model, residueIndex, 'C4')
idx4 = getElementIndexForAtomId(model, residueIndex, 'C2') idx4 = getElementIndexForAtomId(model, residueIndex, 'C2')
idx5 = getElementIndexForAtomId(model, residueIndex, 'N1') idx5 = getElementIndexForAtomId(model, residueIndex, 'N1')
idx6 = getElementIndexForResidueTypeAtomId(model, residueIndex, 'trace') idx6 = getElementIndexForAtomRole(model, residueIndex, 'trace')
} }
if (idx1 !== -1 && idx2 !== -1 && idx3 !== -1 && idx4 !== -1 && idx5 !== -1 && idx6 !== -1) { if (idx1 !== -1 && idx2 !== -1 && idx3 !== -1 && idx4 !== -1 && idx5 !== -1 && idx6 !== -1) {
......
...@@ -4,14 +4,14 @@ ...@@ -4,14 +4,14 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Alexander Rose <alexander.rose@weirdbyte.de>
*/ */
import { Unit, StructureElement, Model, ElementIndex, ResidueIndex } from 'mol-model/structure'; import { Unit, StructureElement, ElementIndex, ResidueIndex } from 'mol-model/structure';
import { Segmentation, OrderedSet, Interval, SortedArray } from 'mol-data/int'; import { Segmentation, OrderedSet, Interval, SortedArray } from 'mol-data/int';
import { MoleculeType, SecondaryStructureType } from 'mol-model/structure/model/types'; import { MoleculeType, SecondaryStructureType, AtomRole } from 'mol-model/structure/model/types';
import Iterator from 'mol-data/iterator'; import Iterator from 'mol-data/iterator';
import { Vec3 } from 'mol-math/linear-algebra'; import { Vec3 } from 'mol-math/linear-algebra';
import SortedRanges from 'mol-data/int/sorted-ranges'; import SortedRanges from 'mol-data/int/sorted-ranges';
import { CoarseSphereConformation, CoarseGaussianConformation } from 'mol-model/structure/model/properties/coarse'; import { CoarseSphereConformation, CoarseGaussianConformation } from 'mol-model/structure/model/properties/coarse';
import { getElementIndexForAtomId, getMoleculeType } from 'mol-model/structure/util'; import { getMoleculeType, getElementIndexForAtomRole } from 'mol-model/structure/util';
export function getPolymerRanges(unit: Unit): SortedRanges<ElementIndex> { export function getPolymerRanges(unit: Unit): SortedRanges<ElementIndex> {
switch (unit.kind) { switch (unit.kind) {
...@@ -68,34 +68,7 @@ export function getPolymerGapCount(unit: Unit) { ...@@ -68,34 +68,7 @@ export function getPolymerGapCount(unit: Unit) {
return count return count
} }
export function getResidueTypeAtomId(moleculeType: MoleculeType, atomType: 'trace' | 'direction') {
switch (moleculeType) {
case MoleculeType.protein:
switch (atomType) {
case 'trace': return 'CA'
case 'direction': return 'O'
}
break
case MoleculeType.RNA:
switch (atomType) {
case 'trace': return 'C4\''
case 'direction': return 'C3\''
}
break
case MoleculeType.DNA:
switch (atomType) {
case 'trace': return 'C3\''
case 'direction': return 'C1\''
}
break
}
return ''
}
export function getElementIndexForResidueTypeAtomId(model: Model, rI: ResidueIndex, atomType: 'trace' | 'direction') {
const atomId = getResidueTypeAtomId(getMoleculeType(model, rI), atomType)
return getElementIndexForAtomId(model, rI, atomId)
}
/** Iterates over consecutive pairs of residues/coarse elements in polymers */ /** Iterates over consecutive pairs of residues/coarse elements in polymers */
export function PolymerBackboneIterator(unit: Unit): Iterator<PolymerBackbonePair> { export function PolymerBackboneIterator(unit: Unit): Iterator<PolymerBackbonePair> {
...@@ -119,21 +92,22 @@ function createPolymerBackbonePair (unit: Unit) { ...@@ -119,21 +92,22 @@ function createPolymerBackbonePair (unit: Unit) {
} }
} }
const enum AtomicPolymerBackboneIteratorState { nextPolymer, firstResidue, nextResidue } const enum AtomicPolymerBackboneIteratorState { nextPolymer, firstResidue, nextResidue, cycle }
export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePair> { export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePair> {
private value: PolymerBackbonePair private value: PolymerBackbonePair
private polymerIt: SortedRanges.Iterator<ElementIndex, ResidueIndex> private polymerIt: SortedRanges.Iterator<ElementIndex, ResidueIndex>
private residueIt: Segmentation.SegmentIterator<ResidueIndex> private residueIt: Segmentation.SegmentIterator<ResidueIndex>
private state: AtomicPolymerBackboneIteratorState = AtomicPolymerBackboneIteratorState.nextPolymer private state: AtomicPolymerBackboneIteratorState = AtomicPolymerBackboneIteratorState.nextPolymer
private residueSegment: Segmentation.Segment<ResidueIndex>
hasNext: boolean = false; hasNext: boolean = false;
private getElementIndex(residueIndex: ResidueIndex, atomType: 'trace' | 'direction') { private getElementIndex(residueIndex: ResidueIndex, atomRole: AtomRole) {
const index = getElementIndexForResidueTypeAtomId(this.unit.model, residueIndex, atomType) const index = getElementIndexForAtomRole(this.unit.model, residueIndex, atomRole)
// TODO handle case when it returns -1 // TODO handle case when it returns -1
const elementIndex = SortedArray.indexOf(this.unit.elements, index) as ElementIndex const elementIndex = SortedArray.indexOf(this.unit.elements, index) as ElementIndex
if (elementIndex === -1) { if (elementIndex === -1) {
console.log('-1', residueIndex, atomType, index) console.log('-1', residueIndex, atomRole, index)
} }
return elementIndex === -1 ? 0 as ElementIndex : elementIndex return elementIndex === -1 ? 0 as ElementIndex : elementIndex
} }
...@@ -141,10 +115,10 @@ export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePa ...@@ -141,10 +115,10 @@ export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePa
move() { move() {
if (this.state === AtomicPolymerBackboneIteratorState.nextPolymer) { if (this.state === AtomicPolymerBackboneIteratorState.nextPolymer) {
while (this.polymerIt.hasNext) { while (this.polymerIt.hasNext) {
const residueSegment = this.polymerIt.move() this.residueIt.setSegment(this.polymerIt.move());
this.residueIt.setSegment(residueSegment);
if (this.residueIt.hasNext) { if (this.residueIt.hasNext) {
this.value.centerB.element = this.getElementIndex(this.residueIt.move().index, 'trace') this.residueSegment = this.residueIt.move()
this.value.centerB.element = this.getElementIndex(this.residueSegment.index, 'trace')
this.state = AtomicPolymerBackboneIteratorState.nextResidue this.state = AtomicPolymerBackboneIteratorState.nextResidue
break break
} }
...@@ -152,15 +126,26 @@ export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePa ...@@ -152,15 +126,26 @@ export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePa
} }
if (this.state === AtomicPolymerBackboneIteratorState.nextResidue) { if (this.state === AtomicPolymerBackboneIteratorState.nextResidue) {
this.residueSegment = this.residueIt.move()
this.value.centerA.element = this.value.centerB.element this.value.centerA.element = this.value.centerB.element
this.value.centerB.element = this.getElementIndex(this.residueIt.move().index, 'trace') this.value.centerB.element = this.getElementIndex(this.residueSegment.index, 'trace')
if (!this.residueIt.hasNext) { if (!this.residueIt.hasNext) {
// TODO need to advance to a polymer that has two or more residues (can't assume it has) if (this.unit.model.atomicHierarchy.cyclicPolymerMap.has(this.residueSegment.index)) {
this.state = AtomicPolymerBackboneIteratorState.nextPolymer this.state = AtomicPolymerBackboneIteratorState.cycle
} else {
// TODO need to advance to a polymer that has two or more residues (can't assume it has)
this.state = AtomicPolymerBackboneIteratorState.nextPolymer
}
} }
} else if (this.state === AtomicPolymerBackboneIteratorState.cycle) {
const { cyclicPolymerMap } = this.unit.model.atomicHierarchy
this.value.centerA.element = this.value.centerB.element
this.value.centerB.element = this.getElementIndex(cyclicPolymerMap.get(this.residueSegment.index)!, 'trace')
// TODO need to advance to a polymer that has two or more residues (can't assume it has)
this.state = AtomicPolymerBackboneIteratorState.nextPolymer
} }
this.hasNext = this.residueIt.hasNext || this.polymerIt.hasNext this.hasNext = this.residueIt.hasNext || this.polymerIt.hasNext || this.state === AtomicPolymerBackboneIteratorState.cycle
return this.value; return this.value;
} }
...@@ -243,12 +228,12 @@ export class AtomicPolymerGapIterator implements Iterator<PolymerGapPair> { ...@@ -243,12 +228,12 @@ export class AtomicPolymerGapIterator implements Iterator<PolymerGapPair> {
private gapIt: SortedRanges.Iterator<ElementIndex, ResidueIndex> private gapIt: SortedRanges.Iterator<ElementIndex, ResidueIndex>
hasNext: boolean = false; hasNext: boolean = false;
private getElementIndex(residueIndex: ResidueIndex, atomType: 'trace' | 'direction') { private getElementIndex(residueIndex: ResidueIndex, atomRole: AtomRole) {
const index = getElementIndexForResidueTypeAtomId(this.unit.model, residueIndex, atomType) const index = getElementIndexForAtomRole(this.unit.model, residueIndex, atomRole)
// TODO handle case when it returns -1 // TODO handle case when it returns -1
const elementIndex = SortedArray.indexOf(this.unit.elements, index) as ElementIndex const elementIndex = SortedArray.indexOf(this.unit.elements, index) as ElementIndex
if (elementIndex === -1) { if (elementIndex === -1) {
console.log('-1', residueIndex, atomType, index) console.log('-1', residueIndex, atomRole, index)
} }
return elementIndex === -1 ? 0 as ElementIndex : elementIndex return elementIndex === -1 ? 0 as ElementIndex : elementIndex
} }
...@@ -363,22 +348,38 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> ...@@ -363,22 +348,38 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
this.residueSegmentMax = index[this.unit.elements[polymerSegment.end - 1]] this.residueSegmentMax = index[this.unit.elements[polymerSegment.end - 1]]
} }
private getAtomIndex(residueIndex: number, atomType: 'trace' | 'direction') { private getAtomIndex(residueIndex: ResidueIndex, atomRole: AtomRole) {
const index = Math.min(Math.max(this.residueSegmentMin, residueIndex), this.residueSegmentMax) const { cyclicPolymerMap } = this.unit.model.atomicHierarchy
return getElementIndexForResidueTypeAtomId(this.unit.model, index as ResidueIndex, atomType) if (residueIndex < this.residueSegmentMin) {
const cyclicIndex = cyclicPolymerMap.get(this.residueSegmentMin)
if (cyclicIndex !== undefined) {
residueIndex = cyclicIndex - (this.residueSegmentMin - residueIndex - 1) as ResidueIndex
} else {
residueIndex = this.residueSegmentMin
}
} else if (residueIndex > this.residueSegmentMax) {
const cyclicIndex = cyclicPolymerMap.get(this.residueSegmentMax)
if (cyclicIndex !== undefined) {
residueIndex = cyclicIndex + (residueIndex - this.residueSegmentMax - 1) as ResidueIndex
} else {
residueIndex = this.residueSegmentMax
}
}
return getElementIndexForAtomRole(this.unit.model, residueIndex as ResidueIndex, atomRole)
} }
private getElementIndex(residueIndex: number, atomType: 'trace' | 'direction') { private getElementIndex(residueIndex: ResidueIndex, atomRole: AtomRole) {
const index = this.getAtomIndex(residueIndex, atomType) const index = this.getAtomIndex(residueIndex, atomRole)
// TODO handle case when it returns -1 // TODO handle case when it returns -1
const elementIndex = SortedArray.indexOf(this.unit.elements, index) as ElementIndex const elementIndex = SortedArray.indexOf(this.unit.elements, index) as ElementIndex
if (elementIndex === -1) { if (elementIndex === -1) {
console.log('-1', residueIndex, atomType, index) console.log('-1', residueIndex, atomRole, index)
} }
return elementIndex === -1 ? 0 as ElementIndex : elementIndex return elementIndex === -1 ? 0 as ElementIndex : elementIndex
} }
private setControlPoint(out: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, residueIndex: number) { private setControlPoint(out: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, residueIndex: ResidueIndex) {
const ss = this.unit.model.properties.secondaryStructure.type[residueIndex] const ss = this.unit.model.properties.secondaryStructure.type[residueIndex]
if (SecondaryStructureType.is(ss, SecondaryStructureType.Flag.Beta)) { if (SecondaryStructureType.is(ss, SecondaryStructureType.Flag.Beta)) {
Vec3.scale(out, Vec3.add(out, p1, Vec3.add(out, p3, Vec3.add(out, p2, p2))), 1/4) Vec3.scale(out, Vec3.add(out, p1, Vec3.add(out, p3, Vec3.add(out, p2, p2))), 1/4)
...@@ -387,15 +388,6 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> ...@@ -387,15 +388,6 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
} }
} }
// private setDirectionVector(out: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, residueIndex: number) {
// const ss = this.unit.model.properties.secondaryStructure.type[residueIndex]
// if (SecondaryStructureType.is(ss, SecondaryStructureType.Flag.Beta)) {
// Vec3.scale(out, Vec3.add(out, p1, Vec3.add(out, p2, p3)), 1/3)
// } else {
// Vec3.copy(out, p2)
// }
// }
move() { move() {
const { residueIt, polymerIt, value } = this const { residueIt, polymerIt, value } = this
...@@ -415,29 +407,27 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> ...@@ -415,29 +407,27 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
const { index: residueIndex } = residueIt.move(); const { index: residueIndex } = residueIt.move();
value.center.element = this.getElementIndex(residueIndex, 'trace') value.center.element = this.getElementIndex(residueIndex, 'trace')
this.pos(this.p0, this.getAtomIndex(residueIndex - 3, 'trace')) this.pos(this.p0, this.getAtomIndex(residueIndex - 3 as ResidueIndex, 'trace'))
this.pos(this.p1, this.getAtomIndex(residueIndex - 2, 'trace')) this.pos(this.p1, this.getAtomIndex(residueIndex - 2 as ResidueIndex, 'trace'))
this.pos(this.p2, this.getAtomIndex(residueIndex - 1, 'trace')) this.pos(this.p2, this.getAtomIndex(residueIndex - 1 as ResidueIndex, 'trace'))
this.pos(this.p3, this.getAtomIndex(residueIndex, 'trace')) this.pos(this.p3, this.getAtomIndex(residueIndex, 'trace'))
this.pos(this.p4, this.getAtomIndex(residueIndex + 1, 'trace')) this.pos(this.p4, this.getAtomIndex(residueIndex + 1 as ResidueIndex, 'trace'))
this.pos(this.p5, this.getAtomIndex(residueIndex + 2, 'trace')) this.pos(this.p5, this.getAtomIndex(residueIndex + 2 as ResidueIndex, 'trace'))
this.pos(this.p6, this.getAtomIndex(residueIndex + 3, 'trace')) this.pos(this.p6, this.getAtomIndex(residueIndex + 3 as ResidueIndex, 'trace'))
// this.pos(this.v01, this.getAtomIndex(residueIndex - 2, 'direction')) // this.pos(this.v01, this.getAtomIndex(residueIndex - 2 as ResidueIndex, 'direction'))
this.pos(this.v12, this.getAtomIndex(residueIndex - 1, 'direction')) this.pos(this.v12, this.getAtomIndex(residueIndex - 1 as ResidueIndex, 'direction'))
this.pos(this.v23, this.getAtomIndex(residueIndex, 'direction')) this.pos(this.v23, this.getAtomIndex(residueIndex, 'direction'))
// this.pos(this.v34, this.getAtomIndex(residueIndex + 1, 'direction')) // this.pos(this.v34, this.getAtomIndex(residueIndex + 1 as ResidueIndex, 'direction'))
this.value.secStrucType = this.unit.model.properties.secondaryStructure.type[residueIndex] this.value.secStrucType = this.unit.model.properties.secondaryStructure.type[residueIndex]
this.setControlPoint(value.t0, this.p0, this.p1, this.p2, residueIndex - 2) this.setControlPoint(value.t0, this.p0, this.p1, this.p2, residueIndex - 2 as ResidueIndex)
this.setControlPoint(value.t1, this.p1, this.p2, this.p3, residueIndex - 1) this.setControlPoint(value.t1, this.p1, this.p2, this.p3, residueIndex - 1 as ResidueIndex)
this.setControlPoint(value.t2, this.p2, this.p3, this.p4, residueIndex) this.setControlPoint(value.t2, this.p2, this.p3, this.p4, residueIndex)
this.setControlPoint(value.t3, this.p3, this.p4, this.p5, residueIndex + 1) this.setControlPoint(value.t3, this.p3, this.p4, this.p5, residueIndex + 1 as ResidueIndex)
this.setControlPoint(value.t4, this.p4, this.p5, this.p6, residueIndex + 2) this.setControlPoint(value.t4, this.p4, this.p5, this.p6, residueIndex + 2 as ResidueIndex)
// this.setDirectionVector(value.d12, this.v01, this.v12, this.v23, residueIndex)
// this.setDirectionVector(value.d23, this.v12, this.v23, this.v34, residueIndex + 1)
Vec3.copy(value.d12, this.v12) Vec3.copy(value.d12, this.v12)
Vec3.copy(value.d23, this.v23) Vec3.copy(value.d23, this.v23)
......
...@@ -91,17 +91,15 @@ export function getAtomicHierarchyAndConformation(format: mmCIF_Format, atom_sit ...@@ -91,17 +91,15 @@ export function getAtomicHierarchyAndConformation(format: mmCIF_Format, atom_sit
}; };
} }
const conformation = getConformation(atom_site)
const hierarchySegments: AtomicSegments = { const hierarchySegments: AtomicSegments = {
residueAtomSegments: Segmentation.ofOffsets(hierarchyOffsets.residues, Interval.ofBounds(0, atom_site._rowCount)), residueAtomSegments: Segmentation.ofOffsets(hierarchyOffsets.residues, Interval.ofBounds(0, atom_site._rowCount)),
chainAtomSegments: Segmentation.ofOffsets(hierarchyOffsets.chains, Interval.ofBounds(0, atom_site._rowCount)), chainAtomSegments: Segmentation.ofOffsets(hierarchyOffsets.chains, Interval.ofBounds(0, atom_site._rowCount)),
} }
const hierarchyKeys = getAtomicKeys(hierarchyData, entities, hierarchySegments); const hierarchyKeys = getAtomicKeys(hierarchyData, entities, hierarchySegments);
const hierarchyRanges = getAtomicRanges(hierarchyData, hierarchySegments, formatData.chemicalComponentMap); const hierarchyRanges = getAtomicRanges(hierarchyData, hierarchySegments, conformation, formatData.chemicalComponentMap);
const hierarchy: AtomicHierarchy = { ...hierarchyData, ...hierarchyKeys, ...hierarchySegments, ...hierarchyRanges }; const hierarchy: AtomicHierarchy = { ...hierarchyData, ...hierarchyKeys, ...hierarchySegments, ...hierarchyRanges };
return { return { sameAsPrevious: false, hierarchy, conformation };
sameAsPrevious: false,
hierarchy,
conformation: getConformation(atom_site)
};
} }
\ No newline at end of file
...@@ -83,6 +83,7 @@ export interface AtomicKeys { ...@@ -83,6 +83,7 @@ export interface AtomicKeys {
export interface AtomicRanges { export interface AtomicRanges {
polymerRanges: SortedRanges<ElementIndex> polymerRanges: SortedRanges<ElementIndex>
gapRanges: SortedRanges<ElementIndex> gapRanges: SortedRanges<ElementIndex>
cyclicPolymerMap: Map<ResidueIndex, ResidueIndex>
} }
type _Hierarchy = AtomicData & AtomicSegments & AtomicKeys & AtomicRanges type _Hierarchy = AtomicData & AtomicSegments & AtomicKeys & AtomicRanges
......
...@@ -8,15 +8,51 @@ import { AtomicSegments } from '../atomic'; ...@@ -8,15 +8,51 @@ import { AtomicSegments } from '../atomic';
import { AtomicData, AtomicRanges } from '../atomic/hierarchy'; import { AtomicData, AtomicRanges } from '../atomic/hierarchy';
import { Segmentation, Interval } from 'mol-data/int'; import { Segmentation, Interval } from 'mol-data/int';
import SortedRanges from 'mol-data/int/sorted-ranges'; import SortedRanges from 'mol-data/int/sorted-ranges';
import { ChemicalComponent } from '../chemical-component'; import { ChemicalComponentMap } from '../chemical-component';
import { MoleculeType, isPolymer } from '../../types'; import { MoleculeType, isPolymer } from '../../types';
import { ElementIndex } from '../../indexing'; import { ElementIndex, ResidueIndex } from '../../indexing';
import { getAtomIdForAtomRole } from '../../../util';
import { AtomicConformation } from '../atomic/conformation';
import { Vec3 } from 'mol-math/linear-algebra';
// TODO add gaps at the ends of the chains by comparing to the polymer sequence data // TODO add gaps at the ends of the chains by comparing to the polymer sequence data
export function getAtomicRanges(data: AtomicData, segments: AtomicSegments, chemicalComponentMap: Map<string, ChemicalComponent>): AtomicRanges { function getMoleculeType(compId: string, chemicalComponentMap: ChemicalComponentMap) {
const cc = chemicalComponentMap.get(compId)
return cc ? cc.moleculeType : MoleculeType.unknown
}
function getElementIndexForAtomId(rI: ResidueIndex, atomId: string, data: AtomicData, segments: AtomicSegments, ): ElementIndex {
const { offsets } = segments.residueAtomSegments
const { label_atom_id } = data.atoms
for (let j = offsets[rI], _j = offsets[rI + 1]; j < _j; j++) {
if (label_atom_id.value(j) === atomId) return j as ElementIndex
}
return offsets[rI] as ElementIndex
}
function areBackboneConnected(riStart: ResidueIndex, riEnd: ResidueIndex, data: AtomicData, segments: AtomicSegments, conformation: AtomicConformation, chemicalComponentMap: ChemicalComponentMap) {
const { label_comp_id } = data.residues
const mtStart = getMoleculeType(label_comp_id.value(riStart), chemicalComponentMap)
const mtEnd = getMoleculeType(label_comp_id.value(riEnd), chemicalComponentMap)
if (!isPolymer(mtStart) || !isPolymer(mtEnd)) return false
const startId = getAtomIdForAtomRole(mtStart, 'backboneStart')
const endId = getAtomIdForAtomRole(mtEnd, 'backboneEnd')
const eiStart = getElementIndexForAtomId(riStart, startId, data, segments)
const eiEnd = getElementIndexForAtomId(riEnd, endId, data, segments)
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])
return Vec3.distance(pStart, pEnd) < 2
}
export function getAtomicRanges(data: AtomicData, segments: AtomicSegments, conformation: AtomicConformation, chemicalComponentMap: ChemicalComponentMap): AtomicRanges {
const polymerRanges: number[] = [] const polymerRanges: number[] = []
const gapRanges: number[] = [] const gapRanges: number[] = []
const cyclicPolymerMap = new Map<ResidueIndex, ResidueIndex>()
const chainIt = Segmentation.transientSegments(segments.chainAtomSegments, Interval.ofBounds(0, data.atoms._rowCount)) const chainIt = Segmentation.transientSegments(segments.chainAtomSegments, Interval.ofBounds(0, data.atoms._rowCount))
const residueIt = Segmentation.transientSegments(segments.residueAtomSegments, Interval.ofBounds(0, data.atoms._rowCount)) const residueIt = Segmentation.transientSegments(segments.residueAtomSegments, Interval.ofBounds(0, data.atoms._rowCount))
const { label_seq_id, label_comp_id } = data.residues const { label_seq_id, label_comp_id } = data.residues
...@@ -34,11 +70,17 @@ export function getAtomicRanges(data: AtomicData, segments: AtomicSegments, chem ...@@ -34,11 +70,17 @@ export function getAtomicRanges(data: AtomicData, segments: AtomicSegments, chem
prevEnd = -1 prevEnd = -1
startIndex = -1 startIndex = -1
const riStart = segments.residueAtomSegments.index[chainSegment.start]
const riEnd = segments.residueAtomSegments.index[chainSegment.end - 1]
if (areBackboneConnected(riStart, riEnd, data, segments, conformation, chemicalComponentMap)) {
cyclicPolymerMap.set(riStart, riEnd)
cyclicPolymerMap.set(riEnd, riStart)
}
while (residueIt.hasNext) { while (residueIt.hasNext) {
const residueSegment = residueIt.move(); const residueSegment = residueIt.move();
const residueIndex = residueSegment.index const residueIndex = residueSegment.index
const cc = chemicalComponentMap.get(label_comp_id.value(residueIndex)) const moleculeType = getMoleculeType(label_comp_id.value(residueIndex), chemicalComponentMap)
const moleculeType = cc ? cc.moleculeType : MoleculeType.unknown
const seqId = label_seq_id.value(residueIndex) const seqId = label_seq_id.value(residueIndex)
if (isPolymer(moleculeType)) { if (isPolymer(moleculeType)) {
if (startIndex !== -1) { if (startIndex !== -1) {
...@@ -67,6 +109,7 @@ export function getAtomicRanges(data: AtomicData, segments: AtomicSegments, chem ...@@ -67,6 +109,7 @@ export function getAtomicRanges(data: AtomicData, segments: AtomicSegments, chem
return { return {
polymerRanges: SortedRanges.ofSortedRanges(polymerRanges as ElementIndex[]), polymerRanges: SortedRanges.ofSortedRanges(polymerRanges as ElementIndex[]),
gapRanges: SortedRanges.ofSortedRanges(gapRanges as ElementIndex[]) gapRanges: SortedRanges.ofSortedRanges(gapRanges as ElementIndex[]),
cyclicPolymerMap
} }
} }
\ No newline at end of file
...@@ -54,6 +54,45 @@ export const enum MoleculeType { ...@@ -54,6 +54,45 @@ export const enum MoleculeType {
saccharide saccharide
} }
const AtomRole = {
trace: '', direction: '', backboneStart: '', backboneEnd: ''
}
export type AtomRole = keyof typeof AtomRole
export const MoleculeTypeAtomRoleId: { [k: number]: { [k in AtomRole]: string } } = {
[MoleculeType.protein]: {
trace: 'CA', // TODO 'BB'
direction: 'O', // TODO 'OC1', 'O1', 'OX1', 'OXT'
backboneStart: 'N',
backboneEnd: 'C'
},
[MoleculeType.RNA]: {
trace: 'C4\'', // TODO 'C4*'
direction: 'C3\'', // 'C3*'
backboneStart: 'P',
backboneEnd: 'O3\'' // TODO 'O3*'
},
[MoleculeType.DNA]: {
trace: 'C3\'', // TODO 'C3*'
direction: 'C1\'', // TODO 'C1*'
backboneStart: 'P',
backboneEnd: 'O3\'' // TODO 'O3*'
}
}
export const ProteinBackboneAtoms = [
'CA', 'C', 'N', 'O',
'O1', 'O2', 'OC1', 'OC2', 'OX1', 'OXT',
'H', 'H1', 'H2', 'H3', 'HA', 'HN',
'BB'
]
export const NucleicBackboneAtoms = [
'P', 'OP1', 'OP2',
'O2\'', 'O3\'', 'O4\'', 'O5\'', 'C1\'', 'C2\'', 'C3\'', 'C4\'', 'C5\'',
'O2*', 'O3*', 'O4*', 'O5*', 'C1*', 'C2*', 'C3*', 'C4*', 'C5*'
]
/** Chemical component types as defined in the mmCIF CCD */ /** Chemical component types as defined in the mmCIF CCD */
export enum ComponentType { export enum ComponentType {
// protein // protein
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
*/ */
import { Model, ResidueIndex, ElementIndex } from './model'; import { Model, ResidueIndex, ElementIndex } from './model';
import { MoleculeType } from './model/types'; import { MoleculeType, AtomRole, MoleculeTypeAtomRoleId } from './model/types';
export function getMoleculeType(model: Model, rI: ResidueIndex) { export function getMoleculeType(model: Model, rI: ResidueIndex) {
const compId = model.atomicHierarchy.residues.label_comp_id.value(rI) const compId = model.atomicHierarchy.residues.label_comp_id.value(rI)
...@@ -14,6 +14,15 @@ export function getMoleculeType(model: Model, rI: ResidueIndex) { ...@@ -14,6 +14,15 @@ export function getMoleculeType(model: Model, rI: ResidueIndex) {
return cc ? cc.moleculeType : MoleculeType.unknown return cc ? cc.moleculeType : MoleculeType.unknown
} }
export function getAtomIdForAtomRole(moleculeType: MoleculeType, atomRole: AtomRole) {
const m = MoleculeTypeAtomRoleId[moleculeType]
if (m !== undefined) {
const a = m[atomRole]
if (a !== undefined) return a
}
return ''
}
export function getElementIndexForAtomId(model: Model, rI: ResidueIndex, atomId: string): ElementIndex { export function getElementIndexForAtomId(model: Model, rI: ResidueIndex, atomId: string): ElementIndex {
const { offsets } = model.atomicHierarchy.residueAtomSegments const { offsets } = model.atomicHierarchy.residueAtomSegments
const { label_atom_id } = model.atomicHierarchy.atoms const { label_atom_id } = model.atomicHierarchy.atoms
...@@ -23,6 +32,11 @@ export function getElementIndexForAtomId(model: Model, rI: ResidueIndex, atomId: ...@@ -23,6 +32,11 @@ export function getElementIndexForAtomId(model: Model, rI: ResidueIndex, atomId:
return offsets[rI] as ElementIndex return offsets[rI] as ElementIndex
} }
export function getElementIndexForAtomRole(model: Model, rI: ResidueIndex, atomRole: AtomRole) {
const atomId = getAtomIdForAtomRole(getMoleculeType(model, rI), atomRole)
return getElementIndexForAtomId(model, rI, atomId)
}
export function residueLabel(model: Model, rI: number) { export function residueLabel(model: Model, rI: number) {
const { residues, chains, residueAtomSegments, chainAtomSegments } = model.atomicHierarchy const { residues, chains, residueAtomSegments, chainAtomSegments } = model.atomicHierarchy
const { label_comp_id, label_seq_id } = residues const { label_comp_id, label_seq_id } = residues
......
...@@ -83,7 +83,7 @@ export class Stage { ...@@ -83,7 +83,7 @@ export class Stage {
// this.loadPdbid('2np2') // dna // this.loadPdbid('2np2') // dna
// this.loadPdbid('1d66') // dna // this.loadPdbid('1d66') // dna
// this.loadPdbid('9dna') // A form dna // this.loadPdbid('9dna') // A form dna
this.loadPdbid('1bna') // B form dna // this.loadPdbid('1bna') // B form dna
// this.loadPdbid('199d') // C form dna // this.loadPdbid('199d') // C form dna
// this.loadPdbid('4lb6') // Z form dna // this.loadPdbid('4lb6') // Z form dna
// this.loadPdbid('1egk') // 4-way dna-rna junction // this.loadPdbid('1egk') // 4-way dna-rna junction
...@@ -91,7 +91,7 @@ export class Stage { ...@@ -91,7 +91,7 @@ export class Stage {
// this.loadPdbid('1xv6') // rna, modified nucleotides // this.loadPdbid('1xv6') // rna, modified nucleotides
// this.loadPdbid('3bbm') // rna with linker // this.loadPdbid('3bbm') // rna with linker
// this.loadPdbid('1gfl') // GFP, flourophore has carbonyl oxygen removed // this.loadPdbid('1gfl') // GFP, flourophore has carbonyl oxygen removed
// this.loadPdbid('1sfi') // contains cyclic peptid this.loadPdbid('1sfi') // contains cyclic peptid
// this.loadPdbid('3sn6') // discontinuous chains // this.loadPdbid('3sn6') // discontinuous chains
// this.loadMmcifUrl(`../../examples/1cbs_full.bcif`) // this.loadMmcifUrl(`../../examples/1cbs_full.bcif`)
// this.loadMmcifUrl(`../../examples/1cbs_updated.cif`) // this.loadMmcifUrl(`../../examples/1cbs_updated.cif`)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment