Browse Source

better handling of missing atoms in trace, block, backbone visuals

Alexander Rose 6 years ago
parent
commit
da738d87e6

+ 13 - 10
src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts

@@ -82,18 +82,21 @@ async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, props:
                     idx6 = getElementIndexForAtomRole(model, residueIndex, 'trace')
                 }
 
-                if (idx1 !== -1 && idx2 !== -1 && idx3 !== -1 && idx4 !== -1 && idx5 !== -1 && idx6 !== -1) {
-                    pos(idx1, p1); pos(idx2, p2); pos(idx3, p3); pos(idx4, p4); pos(idx5, p5); pos(idx6, p6)
-                    Vec3.normalize(v12, Vec3.sub(v12, p2, p1))
-                    Vec3.normalize(v34, Vec3.sub(v34, p4, p3))
-                    Vec3.normalize(vC, Vec3.cross(vC, v12, v34))
-                    Mat4.targetTo(t, p1, p2, vC)
-                    Vec3.scaleAndAdd(center, p1, v12, height / 2 - 0.2)
-                    Mat4.scale(t, t, Vec3.set(sVec, width, depth, height))
-                    Mat4.setTranslation(t, center)
+                if (idx5 !== -1 && idx6 !== -1) {
+                    pos(idx5, p5); pos(idx6, p6)
                     builder.setGroup(SortedArray.findPredecessorIndex(elements, idx6))
-                    builder.add(t, box)
                     addCylinder(builder, p5, p6, 1, { radiusTop: 0.2, radiusBottom: 0.2 })
+                    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))
+                        Vec3.normalize(v34, Vec3.sub(v34, p4, p3))
+                        Vec3.normalize(vC, Vec3.cross(vC, v12, v34))
+                        Mat4.targetTo(t, p1, p2, vC)
+                        Vec3.scaleAndAdd(center, p1, v12, height / 2 - 0.2)
+                        Mat4.scale(t, t, Vec3.set(sVec, width, depth, height))
+                        Mat4.setTranslation(t, center)
+                        builder.add(t, box)
+                    }
                 }
             }
 

+ 10 - 3
src/mol-geo/representation/structure/visual/util/polymer/backbone-iterator.ts

@@ -10,6 +10,7 @@ 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> {
@@ -43,13 +44,19 @@ 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 = getElementIndexForAtomRole(this.unit.model, this.residueSegment.index, 'trace')
+                    this.value.centerB.element = this.getElementIndex(this.residueSegment.index, 'trace')
                     this.state = AtomicPolymerBackboneIteratorState.nextResidue
                     break
                 }
@@ -59,7 +66,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 = getElementIndexForAtomRole(this.unit.model, this.residueSegment.index, 'trace')
+            this.value.centerB.element = this.getElementIndex(this.residueSegment.index, 'trace')
             if (!this.residueIt.hasNext) {
                 if (this.unit.model.atomicHierarchy.cyclicPolymerMap.has(this.residueSegment.index)) {
                     this.state = AtomicPolymerBackboneIteratorState.cycle
@@ -71,7 +78,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 = getElementIndexForAtomRole(this.unit.model, cyclicPolymerMap.get(this.residueSegment.index)!, 'trace')
+            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
         }

+ 16 - 15
src/mol-geo/representation/structure/visual/util/polymer/trace-iterator.ts

@@ -89,24 +89,25 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
         this.residueSegmentMax = index[this.unit.elements[polymerSegment.end - 1]]
     }
 
-    private getAtomIndex(residueIndex: ResidueIndex, atomRole: AtomRole) {
-        const { cyclicPolymerMap } = this.unit.model.atomicHierarchy
+    private getElementIndex(residueIndex: ResidueIndex, atomRole: AtomRole) {
+        const { atomicHierarchy } = this.unit.model
         if (residueIndex < this.residueSegmentMin) {
-            const cyclicIndex = cyclicPolymerMap.get(this.residueSegmentMin)
+            const cyclicIndex = atomicHierarchy.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)
+            const cyclicIndex = atomicHierarchy.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, atomRole)
+        const elementIndex = getElementIndexForAtomRole(this.unit.model, residueIndex, atomRole)
+        return elementIndex === -1 ? atomicHierarchy.residueAtomSegments.offsets[residueIndex] : elementIndex
     }
 
     private setControlPoint(out: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, residueIndex: ResidueIndex) {
@@ -135,19 +136,19 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
 
         if (this.state === AtomicPolymerTraceIteratorState.nextResidue) {
             const { index: residueIndex } = residueIt.move();
-            value.center.element = this.getAtomIndex(residueIndex, 'trace')
+            value.center.element = this.getElementIndex(residueIndex, '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 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.p0, this.getElementIndex(residueIndex - 3 as ResidueIndex, 'trace'))
+            this.pos(this.p1, this.getElementIndex(residueIndex - 2 as ResidueIndex, 'trace'))
+            this.pos(this.p2, this.getElementIndex(residueIndex - 1 as ResidueIndex, 'trace'))
+            this.pos(this.p3, this.getElementIndex(residueIndex, 'trace'))
+            this.pos(this.p4, this.getElementIndex(residueIndex + 1 as ResidueIndex, 'trace'))
+            this.pos(this.p5, this.getElementIndex(residueIndex + 2 as ResidueIndex, 'trace'))
+            this.pos(this.p6, this.getElementIndex(residueIndex + 3 as ResidueIndex, 'trace'))
 
             // 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.v12, this.getElementIndex(residueIndex - 1 as ResidueIndex, 'direction'))
+            this.pos(this.v23, this.getElementIndex(residueIndex, 'direction'))
             // this.pos(this.v34, this.getAtomIndex(residueIndex + 1 as ResidueIndex, 'direction'))
 
             this.value.secStrucType = this.unit.model.properties.secondaryStructure.type[residueIndex]

+ 2 - 2
src/mol-model/structure/util.ts

@@ -26,13 +26,13 @@ export function getAtomIdForAtomRole(moleculeType: MoleculeType, atomRole: AtomR
     return ''
 }
 
-export function getElementIndexForAtomId(model: Model, rI: ResidueIndex, atomId: string): ElementIndex {
+export function getElementIndexForAtomId(model: Model, rI: ResidueIndex, atomId: string): ElementIndex | -1 {
     const { offsets } = model.atomicHierarchy.residueAtomSegments
     const { label_atom_id } = model.atomicHierarchy.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
+    return -1
 }
 
 export function getElementIndexForAtomRole(model: Model, rI: ResidueIndex, atomRole: AtomRole) {