Browse Source

use derived moleculeType and traceElementIndex when possible

Alexander Rose 6 years ago
parent
commit
25fddb0492

+ 5 - 9
src/mol-model/structure/structure/util/nucleotide.ts

@@ -6,15 +6,13 @@
 
 import { Unit, ElementIndex } from 'mol-model/structure';
 import { Segmentation, SortedArray } from 'mol-data/int';
-import { isNucleic, MoleculeType } from 'mol-model/structure/model/types';
-import { getElementIndexForAtomRole } from 'mol-model/structure/util';
+import { isNucleic } from 'mol-model/structure/model/types';
 
 export function getNucleotideElements(unit: Unit.Atomic) {
     const indices: ElementIndex[] = []
     const { elements, model } = unit
-    const { chemicalComponentMap } = model.properties
-    const { chainAtomSegments, residueAtomSegments, residues } = model.atomicHierarchy
-    const { label_comp_id } = residues
+    const { chainAtomSegments, residueAtomSegments } = model.atomicHierarchy
+    const { moleculeType, traceElementIndex } = model.atomicHierarchy.derived.residue
     const chainIt = Segmentation.transientSegments(chainAtomSegments, elements)
     const residueIt = Segmentation.transientSegments(residueAtomSegments, elements)
     while (chainIt.hasNext) {
@@ -22,11 +20,9 @@ export function getNucleotideElements(unit: Unit.Atomic) {
 
         while (residueIt.hasNext) {
             const { index } = residueIt.move();
-            const cc = chemicalComponentMap.get(label_comp_id.value(index))
-            const moleculeType = cc ? cc.moleculeType : MoleculeType.unknown
 
-            if (isNucleic(moleculeType)) {
-                const elementIndex = getElementIndexForAtomRole(model, index, 'trace')
+            if (isNucleic(moleculeType[index])) {
+                const elementIndex = traceElementIndex[index]
                 indices.push(elementIndex === -1 ? residueAtomSegments.offsets[index] : elementIndex)
             }
         }

+ 5 - 4
src/mol-model/structure/structure/util/polymer.ts

@@ -7,12 +7,12 @@
 import { Unit, ElementIndex } from 'mol-model/structure';
 import { Segmentation, OrderedSet, Interval, SortedArray } from 'mol-data/int';
 import SortedRanges from 'mol-data/int/sorted-ranges';
-import { getElementIndexForAtomRole } from 'mol-model/structure/util';
 
 export function getAtomicPolymerElements(unit: Unit.Atomic) {
     const indices: ElementIndex[] = []
     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 residueIt = Segmentation.transientSegments(residueAtomSegments, elements)
     while (polymerIt.hasNext) {
@@ -22,7 +22,7 @@ export function getAtomicPolymerElements(unit: Unit.Atomic) {
             const residueSegment = residueIt.move()
             const { start, end, index } = residueSegment
             if (OrderedSet.areIntersecting(Interval.ofRange(elements[start], elements[end - 1]), elements)) {
-                const elementIndex = getElementIndexForAtomRole(model, index, 'trace')
+                const elementIndex = traceElementIndex[index]
                 indices.push(elementIndex === -1 ? residueAtomSegments.offsets[index] : elementIndex)
             }
         }
@@ -47,13 +47,14 @@ export function getAtomicGapElements(unit: Unit.Atomic) {
     const indices: ElementIndex[] = []
     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);
     while (gapIt.hasNext) {
         const gapSegment = gapIt.move();
         const indexStart = residueIndex[elements[gapSegment.start]]
         const indexEnd = residueIndex[elements[gapSegment.end - 1]]
-        const elementIndexStart = getElementIndexForAtomRole(model, indexStart, 'trace')
-        const elementIndexEnd = getElementIndexForAtomRole(model, indexEnd, 'trace')
+        const elementIndexStart = traceElementIndex[indexStart]
+        const elementIndexEnd = traceElementIndex[indexEnd]
         indices.push(elementIndexStart === -1 ? residueAtomSegments.offsets[indexStart] : elementIndexStart)
         indices.push(elementIndexEnd === -1 ? residueAtomSegments.offsets[indexEnd] : elementIndexEnd)
 

+ 0 - 5
src/mol-model/structure/util.ts

@@ -49,11 +49,6 @@ export function getAtomIdForAtomRole(moleculeType: MoleculeType, atomRole: AtomR
     return ''
 }
 
-export function getElementIndexForAtomRole(model: Model, rI: ResidueIndex, atomRole: AtomRole) {
-    const atomId = getAtomIdForAtomRole(getAtomicMoleculeType(model, rI), atomRole)
-    return model.atomicHierarchy.index.findAtomOnResidue(rI, atomId)
-}
-
 export function residueLabel(model: Model, rI: number) {
     const { residues, chains, residueAtomSegments, chainAtomSegments } = model.atomicHierarchy
     const { label_comp_id, label_seq_id } = residues

+ 18 - 13
src/mol-repr/structure/visual/nucleotide-block-mesh.ts

@@ -8,8 +8,7 @@ import { Unit, Structure, ElementIndex } from 'mol-model/structure';
 import { UnitsVisual } from '../representation';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { Segmentation } from 'mol-data/int';
-import { MoleculeType, isNucleic, isPurinBase, isPyrimidineBase } from 'mol-model/structure/model/types';
-import { getElementIndexForAtomRole } from 'mol-model/structure/util';
+import { isNucleic, isPurinBase, isPyrimidineBase } from 'mol-model/structure/model/types';
 import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual';
 import { NucleotideLocationIterator, markNucleotideElement, getNucleotideElementLoci } from './util/nucleotide';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
@@ -20,6 +19,7 @@ import { addCylinder } from 'mol-geo/geometry/mesh/builder/cylinder';
 import { VisualContext } from 'mol-repr/representation';
 import { Theme } from 'mol-theme/theme';
 import { VisualUpdateState } from 'mol-repr/util';
+import { CylinderProps } from 'mol-geo/primitive/cylinder';
 
 const p1 = Vec3.zero()
 const p2 = Vec3.zero()
@@ -37,6 +37,7 @@ const box = Box()
 
 export const NucleotideBlockMeshParams = {
     sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }),
+    radialSegments: PD.Numeric(16, { min: 3, max: 56, step: 1 }),
 }
 export const DefaultNucleotideBlockMeshProps = PD.getDefaultValues(NucleotideBlockMeshParams)
 export type NucleotideBlockMeshProps = typeof DefaultNucleotideBlockMeshProps
@@ -44,31 +45,35 @@ export type NucleotideBlockMeshProps = typeof DefaultNucleotideBlockMeshProps
 function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: NucleotideBlockMeshProps, mesh?: Mesh) {
     if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
 
-    const { sizeFactor } = props
+    const nucleotideElementCount = unit.nucleotideElements.length
+    if (!nucleotideElementCount) return Mesh.createEmpty(mesh)
 
-    // TODO better vertex count estimate
-    const builder = MeshBuilder.create(256, 128, mesh)
+    const { sizeFactor, radialSegments } = props
+
+    const vertexCount = nucleotideElementCount * (box.vertices.length / 3 + radialSegments * 2)
+    const builder = MeshBuilder.create(vertexCount, vertexCount / 4, mesh)
 
     const { elements, model } = unit
-    const { chemicalComponentMap, modifiedResidues } = model.properties
+    const { modifiedResidues } = model.properties
     const { chainAtomSegments, residueAtomSegments, residues, index: atomicIndex } = model.atomicHierarchy
+    const { moleculeType, traceElementIndex } = model.atomicHierarchy.derived.residue
     const { label_comp_id } = residues
     const pos = unit.conformation.invariantPosition
 
     const chainIt = Segmentation.transientSegments(chainAtomSegments, elements)
     const residueIt = Segmentation.transientSegments(residueAtomSegments, elements)
 
+    const cylinderProps: CylinderProps = { radiusTop: 1 * sizeFactor, radiusBottom: 1 * sizeFactor, radialSegments }
+
     let i = 0
     while (chainIt.hasNext) {
         residueIt.setSegment(chainIt.move());
 
         while (residueIt.hasNext) {
             const { index: residueIndex } = residueIt.move();
-            let compId = label_comp_id.value(residueIndex)
-            const cc = chemicalComponentMap.get(compId)
-            const moleculeType = cc ? cc.moleculeType : MoleculeType.unknown
 
-            if (isNucleic(moleculeType)) {
+            if (isNucleic(moleculeType[residueIndex])) {
+                let compId = label_comp_id.value(residueIndex)
                 const parentId = modifiedResidues.parentId.get(compId)
                 if (parentId !== undefined) compId = parentId
                 let idx1: ElementIndex | -1 = -1, idx2: ElementIndex | -1 = -1, idx3: ElementIndex | -1 = -1, idx4: ElementIndex | -1 = -1, idx5: ElementIndex | -1 = -1, idx6: ElementIndex | -1 = -1
@@ -81,7 +86,7 @@ function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structure: St
                     idx3 = atomicIndex.findAtomOnResidue(residueIndex, 'C6')
                     idx4 = atomicIndex.findAtomOnResidue(residueIndex, 'C2')
                     idx5 = atomicIndex.findAtomOnResidue(residueIndex, 'N9')
-                    idx6 = getElementIndexForAtomRole(model, residueIndex, 'trace')
+                    idx6 = traceElementIndex[residueIndex]
                 } else if (isPyrimidineBase(compId)) {
                     height = 3.0
                     idx1 = atomicIndex.findAtomOnResidue(residueIndex, 'N3')
@@ -89,13 +94,13 @@ function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structure: St
                     idx3 = atomicIndex.findAtomOnResidue(residueIndex, 'C4')
                     idx4 = atomicIndex.findAtomOnResidue(residueIndex, 'C2')
                     idx5 = atomicIndex.findAtomOnResidue(residueIndex, 'N1')
-                    idx6 = getElementIndexForAtomRole(model, residueIndex, 'trace')
+                    idx6 = traceElementIndex[residueIndex]
                 }
 
                 if (idx5 !== -1 && idx6 !== -1) {
                     pos(idx5, p5); pos(idx6, p6)
                     builder.setGroup(i)
-                    addCylinder(builder, p5, p6, 1, { radiusTop: 1 * sizeFactor, radiusBottom: 1 * sizeFactor })
+                    addCylinder(builder, p5, p6, 1, cylinderProps)
                     if (idx1 !== -1 && idx2 !== -1 && idx3 !== -1 && idx4 !== -1) {
                         pos(idx1, p1); pos(idx2, p2); pos(idx3, p3); pos(idx4, p4);
                         Vec3.normalize(v12, Vec3.sub(v12, p2, p1))

+ 3 - 3
src/mol-repr/structure/visual/util/nucleotide.ts

@@ -12,7 +12,6 @@ import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { StructureGroup } from 'mol-repr/structure/units-visual';
 import { getResidueLoci } from './common';
-import { getElementIndexForAtomRole } from 'mol-model/structure/util';
 
 export namespace NucleotideLocationIterator {
     export function fromGroup(group: Unit.SymmetryGroup): LocationIterator {
@@ -50,6 +49,7 @@ export function markNucleotideElement(loci: Loci, structureGroup: StructureGroup
     if (!Unit.isAtomic(unit)) return false
     const { nucleotideElements, model, elements } = unit
     const { index, offsets } = model.atomicHierarchy.residueAtomSegments
+    const { traceElementIndex } = model.atomicHierarchy.derived.residue
     const groupCount = nucleotideElements.length
     for (const e of loci.elements) {
         const unitIdx = group.unitIndexMap.get(e.unit.id)
@@ -61,8 +61,8 @@ export function markNucleotideElement(loci: Loci, structureGroup: StructureGroup
                 const unitIndexMin = OrderedSet.findPredecessorIndex(elements, offsets[rI])
                 const unitIndexMax = OrderedSet.findPredecessorIndex(elements, offsets[rI + 1] - 1)
                 const unitIndexInterval = Interval.ofRange(unitIndexMin, unitIndexMax)
-                if(!OrderedSet.isSubset(e.indices, unitIndexInterval)) return
-                const eI = getElementIndexForAtomRole(model, rI, 'trace')
+                if (!OrderedSet.isSubset(e.indices, unitIndexInterval)) return
+                const eI = traceElementIndex[rI]
                 const idx = OrderedSet.indexOf(eUnit.nucleotideElements, eI)
                 if (idx !== -1) {
                     if (apply(Interval.ofSingleton(unitIdx * groupCount + idx))) changed = true

+ 2 - 2
src/mol-repr/structure/visual/util/polymer.ts

@@ -11,7 +11,6 @@ import { EmptyLoci, Loci } from 'mol-model/loci';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { StructureGroup } from 'mol-repr/structure/units-visual';
-import { getElementIndexForAtomRole } from 'mol-model/structure/util';
 import { getResidueLoci } from './common';
 
 export * from './polymer/backbone-iterator'
@@ -86,6 +85,7 @@ export function markPolymerElement(loci: Loci, structureGroup: StructureGroup, a
     if (loci.structure !== structure) return false
     const { polymerElements, model, elements } = group.units[0]
     const { index, offsets } = model.atomicHierarchy.residueAtomSegments
+    const { traceElementIndex } = model.atomicHierarchy.derived.residue
     const groupCount = polymerElements.length
     for (const e of loci.elements) {
         const unitIdx = group.unitIndexMap.get(e.unit.id)
@@ -97,7 +97,7 @@ export function markPolymerElement(loci: Loci, structureGroup: StructureGroup, a
                 const unitIndexMax = OrderedSet.findPredecessorIndex(elements, offsets[rI + 1] - 1)
                 const unitIndexInterval = Interval.ofRange(unitIndexMin, unitIndexMax)
                 if (!OrderedSet.isSubset(e.indices, unitIndexInterval)) return
-                const eI = getElementIndexForAtomRole(model, rI, 'trace')
+                const eI = traceElementIndex[rI]
                 const idx = OrderedSet.indexOf(e.unit.polymerElements, eI)
                 if (idx !== -1) {
                     if (apply(Interval.ofSingleton(unitIdx * groupCount + idx))) changed = true

+ 5 - 11
src/mol-repr/structure/visual/util/polymer/backbone-iterator.ts

@@ -8,9 +8,7 @@ import { Unit, StructureElement, ElementIndex, ResidueIndex } from 'mol-model/st
 import { Segmentation } from 'mol-data/int';
 import Iterator from 'mol-data/iterator';
 import SortedRanges from 'mol-data/int/sorted-ranges';
-import { getElementIndexForAtomRole } from 'mol-model/structure/util';
 import { getPolymerRanges } from '../polymer';
-import { AtomRole } from 'mol-model/structure/model/types';
 
 /** Iterates over consecutive pairs of residues/coarse elements in polymers */
 export function PolymerBackboneIterator(unit: Unit): Iterator<PolymerBackbonePair> {
@@ -37,6 +35,7 @@ function createPolymerBackbonePair (unit: Unit) {
 const enum AtomicPolymerBackboneIteratorState { nextPolymer, firstResidue, nextResidue, cycle }
 
 export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePair> {
+    private traceElementIndex: ArrayLike<ElementIndex>
     private value: PolymerBackbonePair
     private polymerIt: SortedRanges.Iterator<ElementIndex, ResidueIndex>
     private residueIt: Segmentation.SegmentIterator<ResidueIndex>
@@ -44,19 +43,13 @@ export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePa
     private residueSegment: Segmentation.Segment<ResidueIndex>
     hasNext: boolean = false;
 
-    private getElementIndex(residueIndex: ResidueIndex, atomRole: AtomRole) {
-        const { atomicHierarchy } = this.unit.model
-        const elementIndex = getElementIndexForAtomRole(this.unit.model, residueIndex, atomRole)
-        return elementIndex === -1 ? atomicHierarchy.residueAtomSegments.offsets[residueIndex] : elementIndex
-    }
-
     move() {
         if (this.state === AtomicPolymerBackboneIteratorState.nextPolymer) {
             while (this.polymerIt.hasNext) {
                 this.residueIt.setSegment(this.polymerIt.move());
                 if (this.residueIt.hasNext) {
                     this.residueSegment = this.residueIt.move()
-                    this.value.centerB.element = this.getElementIndex(this.residueSegment.index, 'trace')
+                    this.value.centerB.element = this.traceElementIndex[this.residueSegment.index]
                     this.state = AtomicPolymerBackboneIteratorState.nextResidue
                     break
                 }
@@ -66,7 +59,7 @@ 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.residueSegment.index, 'trace')
+            this.value.centerB.element = this.traceElementIndex[this.residueSegment.index]
             if (!this.residueIt.hasNext) {
                 if (this.unit.model.atomicHierarchy.cyclicPolymerMap.has(this.residueSegment.index)) {
                     this.state = AtomicPolymerBackboneIteratorState.cycle
@@ -78,7 +71,7 @@ export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePa
         } 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')
+            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)
             this.state = AtomicPolymerBackboneIteratorState.nextPolymer
         }
@@ -88,6 +81,7 @@ export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePa
     }
 
     constructor(private unit: Unit.Atomic) {
+        this.traceElementIndex = unit.model.atomicHierarchy.derived.residue.traceElementIndex
         this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements)
         this.residueIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements)
         this.value = createPolymerBackbonePair(unit)

+ 4 - 9
src/mol-repr/structure/visual/util/polymer/gap-iterator.ts

@@ -5,10 +5,8 @@
  */
 
 import { Unit, StructureElement, ElementIndex, ResidueIndex } from 'mol-model/structure';
-import { AtomRole } from 'mol-model/structure/model/types';
 import Iterator from 'mol-data/iterator';
 import SortedRanges from 'mol-data/int/sorted-ranges';
-import { getElementIndexForAtomRole } from 'mol-model/structure/util';
 import { getGapRanges } from '../polymer';
 
 /** Iterates over gaps, i.e. the stem residues/coarse elements adjacent to gaps */
@@ -34,25 +32,22 @@ function createPolymerGapPair (unit: Unit) {
 }
 
 export class AtomicPolymerGapIterator implements Iterator<PolymerGapPair> {
+    private traceElementIndex: ArrayLike<ElementIndex>
     private value: PolymerGapPair
     private gapIt: SortedRanges.Iterator<ElementIndex, ResidueIndex>
     hasNext: boolean = false;
 
-    private getElementIndex(residueIndex: ResidueIndex, atomRole: AtomRole) {
-        const elementIndex = getElementIndexForAtomRole(this.unit.model, residueIndex, atomRole)
-        return elementIndex === -1 ? this.unit.model.atomicHierarchy.residueAtomSegments.offsets[residueIndex] : elementIndex
-    }
-
     move() {
         const { elements, residueIndex } = this.unit
         const gapSegment = this.gapIt.move();
-        this.value.centerA.element = this.getElementIndex(residueIndex[elements[gapSegment.start]], 'trace')
-        this.value.centerB.element = this.getElementIndex(residueIndex[elements[gapSegment.end - 1]], 'trace')
+        this.value.centerA.element = this.traceElementIndex[residueIndex[elements[gapSegment.start]]]
+        this.value.centerB.element = this.traceElementIndex[residueIndex[elements[gapSegment.end - 1]]]
         this.hasNext = this.gapIt.hasNext
         return this.value;
     }
 
     constructor(private unit: Unit.Atomic) {
+        this.traceElementIndex = unit.model.atomicHierarchy.derived.residue.traceElementIndex
         this.gapIt = SortedRanges.transientSegments(getGapRanges(unit), unit.elements);
         this.value = createPolymerGapPair(unit)
         this.hasNext = this.gapIt.hasNext