diff --git a/src/mol-geo/primitive/pyramid.ts b/src/mol-geo/primitive/pyramid.ts index 6258cf6c5911ac3e93d5f830beafa612def99fd0..04f9cc0b4538eb10a8469387efe3120a035dd08a 100644 --- a/src/mol-geo/primitive/pyramid.ts +++ b/src/mol-geo/primitive/pyramid.ts @@ -54,6 +54,12 @@ export function Pyramid(points: ArrayLike<number>): Primitive { return builder.getPrimitive() } +let triangularPyramid: Primitive +export function TriangularPyramid() { + if (!triangularPyramid) triangularPyramid = Pyramid(polygon(3, true)) + return triangularPyramid +} + let octagonalPyramid: Primitive export function OctagonalPyramid() { if (!octagonalPyramid) octagonalPyramid = Pyramid(polygon(8, true)) diff --git a/src/mol-model-formats/structure/mmcif/atomic.ts b/src/mol-model-formats/structure/mmcif/atomic.ts index ec4ea2191d133e82a21ff9e939f0c88f920a1a6c..ba23561126f9d8e2207dfbd7eec7284bddbc880f 100644 --- a/src/mol-model-formats/structure/mmcif/atomic.ts +++ b/src/mol-model-formats/structure/mmcif/atomic.ts @@ -15,7 +15,6 @@ import { AtomicConformation, AtomicData, AtomicHierarchy, AtomicSegments, AtomsS import { getAtomicIndex } from '../../../mol-model/structure/model/properties/utils/atomic-index'; import { ElementSymbol } from '../../../mol-model/structure/model/types'; import { Entities } from '../../../mol-model/structure/model/properties/common'; -import { getAtomicRanges } from '../../../mol-model/structure/model/properties/utils/atomic-ranges'; import { getAtomicDerivedData } from '../../../mol-model/structure/model/properties/utils/atomic-derived'; import { FormatData } from './parser'; @@ -100,7 +99,6 @@ export function getAtomicHierarchyAndConformation(atom_site: AtomSite, sourceInd const index = getAtomicIndex(hierarchyData, entities, hierarchySegments); const derived = getAtomicDerivedData(hierarchyData, index, formatData.chemicalComponentMap); - const hierarchyRanges = getAtomicRanges(hierarchyData, hierarchySegments, conformation, index, derived.residue.moleculeType); - const hierarchy: AtomicHierarchy = { ...hierarchyData, ...hierarchySegments, ...hierarchyRanges, index, derived }; + const hierarchy: AtomicHierarchy = { ...hierarchyData, ...hierarchySegments, index, derived }; return { sameAsPrevious: false, hierarchy, conformation }; } \ No newline at end of file diff --git a/src/mol-model-formats/structure/mmcif/parser.ts b/src/mol-model-formats/structure/mmcif/parser.ts index 7a35b55203149878a443912a8c3c12e9b744f885..d4827cb4198352f86de1fa87db2e88007fca29aa 100644 --- a/src/mol-model-formats/structure/mmcif/parser.ts +++ b/src/mol-model-formats/structure/mmcif/parser.ts @@ -30,6 +30,7 @@ import mmCIF_Format = ModelFormat.mmCIF import { memoize1 } from '../../../mol-util/memoize'; import { ElementIndex, EntityIndex } from '../../../mol-model/structure/model'; import { AtomSiteAnisotrop } from './anisotropic'; +import { getAtomicRanges } from '../../../mol-model/structure/model/properties/utils/atomic-ranges'; export async function _parse_mmCif(format: mmCIF_Format, ctx: RuntimeContext) { const formatData = getFormatData(format) @@ -224,6 +225,9 @@ function createStandardModel(format: mmCIF_Format, atom_site: AtomSite, sourceIn } const coarse = EmptyIHMCoarse; + const sequence = getSequence(format.data, entities, atomic.hierarchy, coarse.hierarchy, formatData.modifiedResidues.parentId) + const atomicRanges = getAtomicRanges(atomic.hierarchy, entities, atomic.conformation, sequence) + const entry = format.data.entry.id.valueKind(0) === Column.ValueKind.Present ? format.data.entry.id.value(0) : format.data._name; @@ -241,9 +245,10 @@ function createStandardModel(format: mmCIF_Format, atom_site: AtomSite, sourceIn modelNum, entities, symmetry: getSymmetry(format), - sequence: getSequence(format.data, entities, atomic.hierarchy, coarse.hierarchy, formatData.modifiedResidues.parentId), + sequence, atomicHierarchy: atomic.hierarchy, atomicConformation: atomic.conformation, + atomicRanges, coarseHierarchy: coarse.hierarchy, coarseConformation: coarse.conformation, properties: { @@ -259,6 +264,9 @@ function createStandardModel(format: mmCIF_Format, atom_site: AtomSite, sourceIn function createModelIHM(format: mmCIF_Format, data: IHMData, formatData: FormatData): Model { const atomic = getAtomicHierarchyAndConformation(data.atom_site, data.atom_site_sourceIndex, data.entities, formatData); const coarse = getIHMCoarse(data, formatData); + const sequence = getSequence(format.data, data.entities, atomic.hierarchy, coarse.hierarchy, formatData.modifiedResidues.parentId) + const atomicRanges = getAtomicRanges(atomic.hierarchy, data.entities, atomic.conformation, sequence) + const entry = format.data.entry.id.valueKind(0) === Column.ValueKind.Present ? format.data.entry.id.value(0) : format.data._name; @@ -278,9 +286,10 @@ function createModelIHM(format: mmCIF_Format, data: IHMData, formatData: FormatD modelNum: data.model_id, entities: data.entities, symmetry: getSymmetry(format), - sequence: getSequence(format.data, data.entities, atomic.hierarchy, coarse.hierarchy, formatData.modifiedResidues.parentId), + sequence, atomicHierarchy: atomic.hierarchy, atomicConformation: atomic.conformation, + atomicRanges, coarseHierarchy: coarse.hierarchy, coarseConformation: coarse.conformation, properties: { diff --git a/src/mol-model/structure/model/model.ts b/src/mol-model/structure/model/model.ts index 5c2a45a64744a1bea3489673a199f20b4d9a6f47..3b0be2a6eb49553bc0ac8c8b640ecdb235b9901c 100644 --- a/src/mol-model/structure/model/model.ts +++ b/src/mol-model/structure/model/model.ts @@ -7,7 +7,7 @@ import UUID from '../../../mol-util/uuid'; import StructureSequence from './properties/sequence'; -import { AtomicHierarchy, AtomicConformation } from './properties/atomic'; +import { AtomicHierarchy, AtomicConformation, AtomicRanges } from './properties/atomic'; import { ModelSymmetry } from './properties/symmetry'; import { CoarseHierarchy, CoarseConformation } from './properties/coarse'; import { Entities, ChemicalComponentMap, MissingResidues } from './properties/common'; @@ -42,6 +42,7 @@ export interface Model extends Readonly<{ atomicHierarchy: AtomicHierarchy, atomicConformation: AtomicConformation, + atomicRanges: AtomicRanges, properties: { /** secondary structure provided by the input file */ diff --git a/src/mol-model/structure/model/properties/atomic/hierarchy.ts b/src/mol-model/structure/model/properties/atomic/hierarchy.ts index 12bac4737e68a5039305568b164b4c7aff5e20f0..ae7337b34afe2b8bba8985620b833eb2e356fff6 100644 --- a/src/mol-model/structure/model/properties/atomic/hierarchy.ts +++ b/src/mol-model/structure/model/properties/atomic/hierarchy.ts @@ -222,7 +222,7 @@ export interface AtomicRanges { cyclicPolymerMap: Map<ResidueIndex, ResidueIndex> } -type _Hierarchy = AtomicData & AtomicSegments & AtomicRanges +type _Hierarchy = AtomicData & AtomicSegments export interface AtomicHierarchy extends _Hierarchy { index: AtomicIndex derived: AtomicDerivedData 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 ab38a48e28231ebbff787985b9e7cb9a74739901..0c098a917acc02166b529be9c00122efcd57568a 100644 --- a/src/mol-model/structure/model/properties/utils/atomic-ranges.ts +++ b/src/mol-model/structure/model/properties/utils/atomic-ranges.ts @@ -1,11 +1,10 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { AtomicSegments } from '../atomic'; -import { AtomicData, AtomicRanges, AtomicIndex } from '../atomic/hierarchy'; +import { AtomicRanges, AtomicIndex, AtomicHierarchy } from '../atomic/hierarchy'; import { Segmentation, Interval } from '../../../../../mol-data/int'; import SortedRanges from '../../../../../mol-data/int/sorted-ranges'; import { MoleculeType, isPolymer } from '../../types'; @@ -13,10 +12,10 @@ import { ElementIndex, ResidueIndex } from '../../indexing'; import { getAtomIdForAtomRole } from '../../../util'; import { AtomicConformation } from '../atomic/conformation'; import { Vec3 } from '../../../../../mol-math/linear-algebra'; +import { Entities } from '../common'; +import StructureSequence from '../sequence'; -// TODO add gaps at the ends of the chains by comparing to the polymer sequence data - -function areBackboneConnected(riStart: ResidueIndex, riEnd: ResidueIndex, data: AtomicData, segments: AtomicSegments, conformation: AtomicConformation, index: AtomicIndex, moleculeType: ArrayLike<MoleculeType>) { +function areBackboneConnected(riStart: ResidueIndex, riEnd: ResidueIndex, conformation: AtomicConformation, index: AtomicIndex, moleculeType: ArrayLike<MoleculeType>) { const mtStart = moleculeType[riStart] const mtEnd = moleculeType[riEnd] if (!isPolymer(mtStart) || !isPolymer(mtEnd)) return false @@ -35,13 +34,16 @@ function areBackboneConnected(riStart: ResidueIndex, riEnd: ResidueIndex, data: return Vec3.distance(pStart, pEnd) < 10 // TODO better distance check, take into account if protein/nucleic and if coarse } -export function getAtomicRanges(data: AtomicData, segments: AtomicSegments, conformation: AtomicConformation, index: AtomicIndex, moleculeType: ArrayLike<MoleculeType>): AtomicRanges { +export function getAtomicRanges(hierarchy: AtomicHierarchy, entities: Entities, conformation: AtomicConformation, sequence: StructureSequence): 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 } = data.residues + 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: number let prevStart: number @@ -56,18 +58,24 @@ export function getAtomicRanges(data: AtomicData, segments: AtomicSegments, conf 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, index, moleculeType)) { + const riStart = hierarchy.residueAtomSegments.index[chainSegment.start] + const riEnd = hierarchy.residueAtomSegments.index[chainSegment.end - 1] + if (areBackboneConnected(riStart, riEnd, conformation, index, moleculeType)) { cyclicPolymerMap.set(riStart, riEnd) cyclicPolymerMap.set(riEnd, riStart) } + // TODO + // 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 + while (residueIt.hasNext) { const residueSegment = residueIt.move(); const residueIndex = residueSegment.index const seqId = label_seq_id.value(residueIndex) - if (isPolymer(moleculeType[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) @@ -75,16 +83,24 @@ export function getAtomicRanges(data: AtomicData, segments: AtomicSegments, conf startIndex = residueSegment.start } else if (!residueIt.hasNext) { polymerRanges.push(startIndex, residueSegment.end - 1) + // TODO + // if (seqId !== maxSeqId) { + // gapRanges.push(residueSegment.end - 1, residueSegment.end - 1) + // } } else { - const riStart = segments.residueAtomSegments.index[residueSegment.start] - const riEnd = segments.residueAtomSegments.index[prevEnd - 1] - if (!areBackboneConnected(riStart, riEnd, data, segments, conformation, index, moleculeType)) { + const riStart = hierarchy.residueAtomSegments.index[residueSegment.start] + const riEnd = hierarchy.residueAtomSegments.index[prevEnd - 1] + if (!areBackboneConnected(riStart, riEnd, conformation, hierarchy.index, moleculeType)) { polymerRanges.push(startIndex, prevEnd - 1) startIndex = residueSegment.start } } } else { startIndex = residueSegment.start // start polymer + // TODO + // if (seqId !== 1) { + // gapRanges.push(residueSegment.start, residueSegment.start) + // } } } else { if (startIndex !== -1) { diff --git a/src/mol-model/structure/structure/util/polymer.ts b/src/mol-model/structure/structure/util/polymer.ts index 261cd942d56606756df7e2d354a58471f4e44415..02ca015b771978b4ee16f9d4a7d063bfc6ff00d1 100644 --- a/src/mol-model/structure/structure/util/polymer.ts +++ b/src/mol-model/structure/structure/util/polymer.ts @@ -14,7 +14,7 @@ export function getAtomicPolymerElements(unit: Unit.Atomic) { const { elements, model } = unit const { residueAtomSegments } = unit.model.atomicHierarchy const { traceElementIndex } = model.atomicHierarchy.derived.residue - const polymerIt = SortedRanges.transientSegments(unit.model.atomicHierarchy.polymerRanges, elements) + const polymerIt = SortedRanges.transientSegments(unit.model.atomicRanges.polymerRanges, elements) const residueIt = Segmentation.transientSegments(residueAtomSegments, elements) while (polymerIt.hasNext) { const polymerSegment = polymerIt.move() @@ -51,7 +51,7 @@ export function getAtomicGapElements(unit: Unit.Atomic) { const { elements, model, residueIndex } = unit const { residueAtomSegments } = unit.model.atomicHierarchy const { traceElementIndex } = model.atomicHierarchy.derived.residue - const gapIt = SortedRanges.transientSegments(unit.model.atomicHierarchy.gapRanges, unit.elements); + const gapIt = SortedRanges.transientSegments(unit.model.atomicRanges.gapRanges, unit.elements); while (gapIt.hasNext) { const gapSegment = gapIt.move(); const indexStart = residueIndex[elements[gapSegment.start]] diff --git a/src/mol-repr/structure/visual/polymer-gap-cylinder.ts b/src/mol-repr/structure/visual/polymer-gap-cylinder.ts index 5c662460e7a27b0f9ca99955253ccf1f9f41e38b..160010b4a97a8389434431b9bd824812c61da79f 100644 --- a/src/mol-repr/structure/visual/polymer-gap-cylinder.ts +++ b/src/mol-repr/structure/visual/polymer-gap-cylinder.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -13,11 +13,11 @@ import { MeshBuilder } from '../../../mol-geo/geometry/mesh/mesh-builder'; import { Vec3 } from '../../../mol-math/linear-algebra'; import { CylinderProps } from '../../../mol-geo/primitive/cylinder'; import { PolymerGapIterator, PolymerGapLocationIterator, getPolymerGapElementLoci, eachPolymerGapElement } from './util/polymer'; -import { addSphere } from '../../../mol-geo/geometry/mesh/builder/sphere'; import { addFixedCountDashedCylinder } from '../../../mol-geo/geometry/mesh/builder/cylinder'; import { UnitsMeshParams, UnitsVisual, UnitsMeshVisual } from '../units-visual'; import { LinkCylinderParams } from './util/link'; import { VisualUpdateState } from '../../util'; +// import { TriangularPyramid } from '../../../mol-geo/primitive/pyramid'; const segmentCount = 10 @@ -28,6 +28,10 @@ export const PolymerGapCylinderParams = { export const DefaultPolymerGapCylinderProps = PD.getDefaultValues(PolymerGapCylinderParams) export type PolymerGapCylinderProps = typeof DefaultPolymerGapCylinderProps +// const triangularPyramid = TriangularPyramid() +// const t = Mat4.identity() +// const pd = Vec3.zero() + function createPolymerGapCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PolymerGapCylinderProps, mesh?: Mesh) { const polymerGapCount = unit.gapElements.length if (!polymerGapCount) return Mesh.createEmpty(mesh) @@ -49,9 +53,14 @@ function createPolymerGapCylinderMesh(ctx: VisualContext, unit: Unit, structure: while (polymerGapIt.hasNext) { const { centerA, centerB } = polymerGapIt.move() if (centerA.element === centerB.element) { - builderState.currentGroup = i - pos(centerA.element, pA) - addSphere(builderState, pA, 0.6, 0) + // TODO + // builderState.currentGroup = i + // pos(centerA.element, pA) + // Vec3.add(pd, pA, Vec3.create(1, 0, 0)) + // Mat4.targetTo(t, pA, pd, Vec3.create(0, 1, 0)) + // Mat4.setTranslation(t, pA) + // Mat4.scale(t, t, Vec3.create(0.7, 0.7, 2.5)) + // MeshBuilder.addPrimitive(builderState, t, triangularPyramid) } else { pos(centerA.element, pA) pos(centerB.element, pB) diff --git a/src/mol-repr/structure/visual/util/polymer.ts b/src/mol-repr/structure/visual/util/polymer.ts index 5fac3e83b765cb38e0a351b4ebcb70c707ba4a14..e929e844b1e545597da539d7171422c75abfaaaa 100644 --- a/src/mol-repr/structure/visual/util/polymer.ts +++ b/src/mol-repr/structure/visual/util/polymer.ts @@ -26,7 +26,7 @@ export const OverhangFactor = 2 export function getPolymerRanges(unit: Unit): SortedRanges<ElementIndex> { switch (unit.kind) { - case Unit.Kind.Atomic: return unit.model.atomicHierarchy.polymerRanges + case Unit.Kind.Atomic: return unit.model.atomicRanges.polymerRanges case Unit.Kind.Spheres: return unit.model.coarseHierarchy.spheres.polymerRanges case Unit.Kind.Gaussians: return unit.model.coarseHierarchy.gaussians.polymerRanges } @@ -34,7 +34,7 @@ export function getPolymerRanges(unit: Unit): SortedRanges<ElementIndex> { export function getGapRanges(unit: Unit): SortedRanges<ElementIndex> { switch (unit.kind) { - case Unit.Kind.Atomic: return unit.model.atomicHierarchy.gapRanges + case Unit.Kind.Atomic: return unit.model.atomicRanges.gapRanges case Unit.Kind.Spheres: return unit.model.coarseHierarchy.spheres.gapRanges case Unit.Kind.Gaussians: return unit.model.coarseHierarchy.gaussians.gapRanges } diff --git a/src/mol-repr/structure/visual/util/polymer/backbone-iterator.ts b/src/mol-repr/structure/visual/util/polymer/backbone-iterator.ts index 42db2945f5522c3f781c3b81613711d0fbdbd0c4..82155734d9d95f0cbe9ab739ddd7989e4a50a83a 100644 --- a/src/mol-repr/structure/visual/util/polymer/backbone-iterator.ts +++ b/src/mol-repr/structure/visual/util/polymer/backbone-iterator.ts @@ -61,7 +61,7 @@ export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePa this.value.centerA.element = this.value.centerB.element this.value.centerB.element = this.traceElementIndex[this.residueSegment.index] if (!this.residueIt.hasNext) { - if (this.unit.model.atomicHierarchy.cyclicPolymerMap.has(this.residueSegment.index)) { + if (this.unit.model.atomicRanges.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) @@ -69,7 +69,7 @@ export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePa } } } else if (this.state === AtomicPolymerBackboneIteratorState.cycle) { - const { cyclicPolymerMap } = this.unit.model.atomicHierarchy + const { cyclicPolymerMap } = this.unit.model.atomicRanges this.value.centerA.element = this.value.centerB.element this.value.centerB.element = this.traceElementIndex[cyclicPolymerMap.get(this.residueSegment.index)!] // TODO need to advance to a polymer that has two or more residues (can't assume it has) diff --git a/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts b/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts index 889c57ab7f373a439e97868d032c1c3f5ab6fa1c..d8964ad78ab6b28e2e627b9ebdb39d522c45a61f 100644 --- a/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts +++ b/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts @@ -296,12 +296,12 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> constructor(private unit: Unit.Atomic, structure: Structure) { this.atomicConformation = unit.model.atomicConformation this.residueAtomSegments = unit.model.atomicHierarchy.residueAtomSegments - this.polymerRanges = unit.model.atomicHierarchy.polymerRanges + this.polymerRanges = unit.model.atomicRanges.polymerRanges this.traceElementIndex = unit.model.atomicHierarchy.derived.residue.traceElementIndex as ArrayLike<ElementIndex> // can assume it won't be -1 for polymer residues this.directionFromElementIndex = unit.model.atomicHierarchy.derived.residue.directionFromElementIndex this.directionToElementIndex = unit.model.atomicHierarchy.derived.residue.directionToElementIndex this.moleculeType = unit.model.atomicHierarchy.derived.residue.moleculeType - this.cyclicPolymerMap = unit.model.atomicHierarchy.cyclicPolymerMap + this.cyclicPolymerMap = unit.model.atomicRanges.cyclicPolymerMap this.polymerIt = SortedRanges.transientSegments(this.polymerRanges, unit.elements) this.residueIt = Segmentation.transientSegments(this.residueAtomSegments, unit.elements); this.value = createPolymerTraceElement(unit)