Browse Source

wip, terminal gaps

Alexander Rose 5 years ago
parent
commit
7dd7a117cb

+ 6 - 0
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))

+ 1 - 3
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 };
 }

+ 11 - 2
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: {

+ 2 - 1
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 */

+ 1 - 1
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

+ 33 - 17
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) {

+ 2 - 2
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]]

+ 14 - 5
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)

+ 2 - 2
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
     }

+ 2 - 2
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)

+ 2 - 2
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)