diff --git a/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts b/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts index 790a51a9ed04e4d02d90748d8872fc9bb0f40a98..69fe8db7a0e47b3d9847764f367b94df952a2557 100644 --- a/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts +++ b/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts @@ -25,8 +25,7 @@ import { getElementLoci, markElement } from './util/element'; import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import { Segmentation, SortedArray } from 'mol-data/int'; import { MoleculeType, isNucleic, isPurinBase, isPyrimidineBase } from 'mol-model/structure/model/types'; -import { getElementIndexForAtomId } from 'mol-model/structure/util'; -import { getElementIndexForResidueTypeAtomId } from './util/polymer'; +import { getElementIndexForAtomId, getElementIndexForAtomRole } from 'mol-model/structure/util'; async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, mesh?: Mesh) { if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh) @@ -77,7 +76,7 @@ async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, mesh?: idx3 = getElementIndexForAtomId(model, residueIndex, 'C6') idx4 = getElementIndexForAtomId(model, residueIndex, 'C2') idx5 = getElementIndexForAtomId(model, residueIndex, 'N9') - idx6 = getElementIndexForResidueTypeAtomId(model, residueIndex, 'trace') + idx6 = getElementIndexForAtomRole(model, residueIndex, 'trace') } else if (isPyrimidineBase(compId)) { height = 3.0 idx1 = getElementIndexForAtomId(model, residueIndex, 'N3') @@ -85,7 +84,7 @@ async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, mesh?: idx3 = getElementIndexForAtomId(model, residueIndex, 'C4') idx4 = getElementIndexForAtomId(model, residueIndex, 'C2') 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) { diff --git a/src/mol-geo/representation/structure/visual/util/polymer.ts b/src/mol-geo/representation/structure/visual/util/polymer.ts index 18186967701321c19269be43fb7e32b72973c379..6b664c894c4b608357025c2efc60ca2eb98562c8 100644 --- a/src/mol-geo/representation/structure/visual/util/polymer.ts +++ b/src/mol-geo/representation/structure/visual/util/polymer.ts @@ -4,14 +4,14 @@ * @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 { 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 { Vec3 } from 'mol-math/linear-algebra'; import SortedRanges from 'mol-data/int/sorted-ranges'; 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> { switch (unit.kind) { @@ -68,34 +68,7 @@ export function getPolymerGapCount(unit: Unit) { 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 */ export function PolymerBackboneIterator(unit: Unit): Iterator<PolymerBackbonePair> { @@ -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> { private value: PolymerBackbonePair private polymerIt: SortedRanges.Iterator<ElementIndex, ResidueIndex> private residueIt: Segmentation.SegmentIterator<ResidueIndex> private state: AtomicPolymerBackboneIteratorState = AtomicPolymerBackboneIteratorState.nextPolymer + private residueSegment: Segmentation.Segment<ResidueIndex> hasNext: boolean = false; - private getElementIndex(residueIndex: ResidueIndex, atomType: 'trace' | 'direction') { - const index = getElementIndexForResidueTypeAtomId(this.unit.model, residueIndex, atomType) + private getElementIndex(residueIndex: ResidueIndex, atomRole: AtomRole) { + const index = getElementIndexForAtomRole(this.unit.model, residueIndex, atomRole) // TODO handle case when it returns -1 const elementIndex = SortedArray.indexOf(this.unit.elements, index) as ElementIndex if (elementIndex === -1) { - console.log('-1', residueIndex, atomType, index) + console.log('-1', residueIndex, atomRole, index) } return elementIndex === -1 ? 0 as ElementIndex : elementIndex } @@ -141,10 +115,10 @@ export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePa move() { if (this.state === AtomicPolymerBackboneIteratorState.nextPolymer) { while (this.polymerIt.hasNext) { - const residueSegment = this.polymerIt.move() - this.residueIt.setSegment(residueSegment); + this.residueIt.setSegment(this.polymerIt.move()); 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 break } @@ -152,15 +126,26 @@ export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePa } if (this.state === AtomicPolymerBackboneIteratorState.nextResidue) { + this.residueSegment = this.residueIt.move() 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) { - // TODO need to advance to a polymer that has two or more residues (can't assume it has) - this.state = AtomicPolymerBackboneIteratorState.nextPolymer + if (this.unit.model.atomicHierarchy.cyclicPolymerMap.has(this.residueSegment.index)) { + 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; } @@ -243,12 +228,12 @@ export class AtomicPolymerGapIterator implements Iterator<PolymerGapPair> { private gapIt: SortedRanges.Iterator<ElementIndex, ResidueIndex> hasNext: boolean = false; - private getElementIndex(residueIndex: ResidueIndex, atomType: 'trace' | 'direction') { - const index = getElementIndexForResidueTypeAtomId(this.unit.model, residueIndex, atomType) + private getElementIndex(residueIndex: ResidueIndex, atomRole: AtomRole) { + const index = getElementIndexForAtomRole(this.unit.model, residueIndex, atomRole) // TODO handle case when it returns -1 const elementIndex = SortedArray.indexOf(this.unit.elements, index) as ElementIndex if (elementIndex === -1) { - console.log('-1', residueIndex, atomType, index) + console.log('-1', residueIndex, atomRole, index) } return elementIndex === -1 ? 0 as ElementIndex : elementIndex } @@ -363,22 +348,38 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> this.residueSegmentMax = index[this.unit.elements[polymerSegment.end - 1]] } - private getAtomIndex(residueIndex: number, atomType: 'trace' | 'direction') { - const index = Math.min(Math.max(this.residueSegmentMin, residueIndex), this.residueSegmentMax) - return getElementIndexForResidueTypeAtomId(this.unit.model, index as ResidueIndex, atomType) + private getAtomIndex(residueIndex: ResidueIndex, atomRole: AtomRole) { + const { cyclicPolymerMap } = this.unit.model.atomicHierarchy + 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') { - const index = this.getAtomIndex(residueIndex, atomType) + private getElementIndex(residueIndex: ResidueIndex, atomRole: AtomRole) { + const index = this.getAtomIndex(residueIndex, atomRole) // TODO handle case when it returns -1 const elementIndex = SortedArray.indexOf(this.unit.elements, index) as ElementIndex if (elementIndex === -1) { - console.log('-1', residueIndex, atomType, index) + console.log('-1', residueIndex, atomRole, index) } 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] 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) @@ -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() { const { residueIt, polymerIt, value } = this @@ -415,29 +407,27 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> const { index: residueIndex } = residueIt.move(); value.center.element = this.getElementIndex(residueIndex, 'trace') - this.pos(this.p0, this.getAtomIndex(residueIndex - 3, 'trace')) - this.pos(this.p1, this.getAtomIndex(residueIndex - 2, 'trace')) - this.pos(this.p2, this.getAtomIndex(residueIndex - 1, 'trace')) + this.pos(this.p0, this.getAtomIndex(residueIndex - 3 as ResidueIndex, 'trace')) + this.pos(this.p1, this.getAtomIndex(residueIndex - 2 as ResidueIndex, 'trace')) + this.pos(this.p2, this.getAtomIndex(residueIndex - 1 as ResidueIndex, 'trace')) this.pos(this.p3, this.getAtomIndex(residueIndex, 'trace')) - this.pos(this.p4, this.getAtomIndex(residueIndex + 1, 'trace')) - this.pos(this.p5, this.getAtomIndex(residueIndex + 2, 'trace')) - this.pos(this.p6, this.getAtomIndex(residueIndex + 3, 'trace')) + this.pos(this.p4, this.getAtomIndex(residueIndex + 1 as ResidueIndex, 'trace')) + this.pos(this.p5, this.getAtomIndex(residueIndex + 2 as ResidueIndex, 'trace')) + this.pos(this.p6, this.getAtomIndex(residueIndex + 3 as ResidueIndex, 'trace')) - // this.pos(this.v01, this.getAtomIndex(residueIndex - 2, 'direction')) - this.pos(this.v12, this.getAtomIndex(residueIndex - 1, 'direction')) + // this.pos(this.v01, this.getAtomIndex(residueIndex - 2 as ResidueIndex, 'direction')) + this.pos(this.v12, this.getAtomIndex(residueIndex - 1 as 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.setControlPoint(value.t0, this.p0, this.p1, this.p2, residueIndex - 2) - this.setControlPoint(value.t1, this.p1, this.p2, this.p3, residueIndex - 1) + 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 as 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.t4, this.p4, this.p5, this.p6, residueIndex + 2) + 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 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.d23, this.v23) diff --git a/src/mol-model/structure/model/formats/mmcif/atomic.ts b/src/mol-model/structure/model/formats/mmcif/atomic.ts index db05907fcb8ee28852f7d1abbf93e883ea11c8e9..005dfc7ff933d59a48d29b6a075e33fa417ecbf8 100644 --- a/src/mol-model/structure/model/formats/mmcif/atomic.ts +++ b/src/mol-model/structure/model/formats/mmcif/atomic.ts @@ -91,17 +91,15 @@ export function getAtomicHierarchyAndConformation(format: mmCIF_Format, atom_sit }; } + const conformation = getConformation(atom_site) + const hierarchySegments: AtomicSegments = { residueAtomSegments: Segmentation.ofOffsets(hierarchyOffsets.residues, Interval.ofBounds(0, atom_site._rowCount)), chainAtomSegments: Segmentation.ofOffsets(hierarchyOffsets.chains, Interval.ofBounds(0, atom_site._rowCount)), } 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 }; - return { - sameAsPrevious: false, - hierarchy, - conformation: getConformation(atom_site) - }; + return { sameAsPrevious: false, hierarchy, conformation }; } \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/atomic/hierarchy.ts b/src/mol-model/structure/model/properties/atomic/hierarchy.ts index 33d98c72d09a399e19d76f9810b13f1d5ebc6731..d4db8be333386f1306dbbc422011262412819842 100644 --- a/src/mol-model/structure/model/properties/atomic/hierarchy.ts +++ b/src/mol-model/structure/model/properties/atomic/hierarchy.ts @@ -83,6 +83,7 @@ export interface AtomicKeys { export interface AtomicRanges { polymerRanges: SortedRanges<ElementIndex> gapRanges: SortedRanges<ElementIndex> + cyclicPolymerMap: Map<ResidueIndex, ResidueIndex> } type _Hierarchy = AtomicData & AtomicSegments & AtomicKeys & AtomicRanges diff --git a/src/mol-model/structure/model/properties/utils/atomic-ranges.ts b/src/mol-model/structure/model/properties/utils/atomic-ranges.ts index cef6e3c7a42a457f9f4460cc5842d93dfe4f9f9c..52db07fc11d0cb33654db16bef3e6ddb83b52713 100644 --- a/src/mol-model/structure/model/properties/utils/atomic-ranges.ts +++ b/src/mol-model/structure/model/properties/utils/atomic-ranges.ts @@ -8,15 +8,51 @@ import { AtomicSegments } from '../atomic'; import { AtomicData, AtomicRanges } from '../atomic/hierarchy'; import { Segmentation, Interval } from 'mol-data/int'; import SortedRanges from 'mol-data/int/sorted-ranges'; -import { ChemicalComponent } from '../chemical-component'; +import { ChemicalComponentMap } from '../chemical-component'; 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 -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 gapRanges: number[] = [] + const cyclicPolymerMap = new Map<ResidueIndex, ResidueIndex>() 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 { label_seq_id, label_comp_id } = data.residues @@ -34,11 +70,17 @@ export function getAtomicRanges(data: AtomicData, segments: AtomicSegments, chem prevEnd = -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) { const residueSegment = residueIt.move(); const residueIndex = residueSegment.index - const cc = chemicalComponentMap.get(label_comp_id.value(residueIndex)) - const moleculeType = cc ? cc.moleculeType : MoleculeType.unknown + const moleculeType = getMoleculeType(label_comp_id.value(residueIndex), chemicalComponentMap) const seqId = label_seq_id.value(residueIndex) if (isPolymer(moleculeType)) { if (startIndex !== -1) { @@ -67,6 +109,7 @@ export function getAtomicRanges(data: AtomicData, segments: AtomicSegments, chem return { 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 diff --git a/src/mol-model/structure/model/types.ts b/src/mol-model/structure/model/types.ts index cced8c8808d71d137ffbd8b352fd3f2b42a09d7e..1492b2e05ada7a7fe0ab10b648719305e48736d7 100644 --- a/src/mol-model/structure/model/types.ts +++ b/src/mol-model/structure/model/types.ts @@ -54,6 +54,45 @@ export const enum MoleculeType { 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 */ export enum ComponentType { // protein diff --git a/src/mol-model/structure/util.ts b/src/mol-model/structure/util.ts index eab551c3f9cb9d71e15c42d8e3c05a1b48a15d31..fbe525649c4e876ffe7eb21d39c805d030e33af7 100644 --- a/src/mol-model/structure/util.ts +++ b/src/mol-model/structure/util.ts @@ -5,7 +5,7 @@ */ 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) { const compId = model.atomicHierarchy.residues.label_comp_id.value(rI) @@ -14,6 +14,15 @@ export function getMoleculeType(model: Model, rI: ResidueIndex) { 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 { const { offsets } = model.atomicHierarchy.residueAtomSegments const { label_atom_id } = model.atomicHierarchy.atoms @@ -23,6 +32,11 @@ export function getElementIndexForAtomId(model: Model, rI: ResidueIndex, atomId: 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) { const { residues, chains, residueAtomSegments, chainAtomSegments } = model.atomicHierarchy const { label_comp_id, label_seq_id } = residues diff --git a/src/mol-view/stage.ts b/src/mol-view/stage.ts index ddd499c4b2d34ae291c606beacb9f196791a6b25..992773ebbcb7ebabebc69f38c9621482cd026112 100644 --- a/src/mol-view/stage.ts +++ b/src/mol-view/stage.ts @@ -83,7 +83,7 @@ export class Stage { // this.loadPdbid('2np2') // dna // this.loadPdbid('1d66') // 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('4lb6') // Z form dna // this.loadPdbid('1egk') // 4-way dna-rna junction @@ -91,7 +91,7 @@ export class Stage { // this.loadPdbid('1xv6') // rna, modified nucleotides // this.loadPdbid('3bbm') // rna with linker // 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.loadMmcifUrl(`../../examples/1cbs_full.bcif`) // this.loadMmcifUrl(`../../examples/1cbs_updated.cif`)