From 9b3d2f396efc658b84d50b8aab948cbb51812f4c Mon Sep 17 00:00:00 2001
From: Alexander Rose <alexander.rose@weirdbyte.de>
Date: Wed, 12 Jun 2019 23:14:05 -0700
Subject: [PATCH] polymer-trace improvements

---
 .../structure/visual/polymer-trace-mesh.ts    |  2 +-
 .../visual/util/polymer/curve-segment.ts      | 50 ++++++-------
 .../visual/util/polymer/trace-iterator.ts     | 71 ++++++++++++-------
 3 files changed, 74 insertions(+), 49 deletions(-)

diff --git a/src/mol-repr/structure/visual/polymer-trace-mesh.ts b/src/mol-repr/structure/visual/polymer-trace-mesh.ts
index 16793765b..4c94290bb 100644
--- a/src/mol-repr/structure/visual/polymer-trace-mesh.ts
+++ b/src/mol-repr/structure/visual/polymer-trace-mesh.ts
@@ -52,7 +52,7 @@ function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: Struc
         const isNucleicType = isNucleic(v.moleculeType)
         const isSheet = SecondaryStructureType.is(v.secStrucType, SecondaryStructureType.Flag.Beta)
         const isHelix = SecondaryStructureType.is(v.secStrucType, SecondaryStructureType.Flag.Helix)
-        const tension = isNucleicType ? 0.5 : 0.9
+        const tension = isHelix ? 0.9 : 0.5
         const shift = isNucleicType ? 0.3 : 0.5
 
         interpolateCurveSegment(state, v, tension, shift)
diff --git a/src/mol-repr/structure/visual/util/polymer/curve-segment.ts b/src/mol-repr/structure/visual/util/polymer/curve-segment.ts
index b096422dc..ebb7aaf3d 100644
--- a/src/mol-repr/structure/visual/util/polymer/curve-segment.ts
+++ b/src/mol-repr/structure/visual/util/polymer/curve-segment.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>
  */
@@ -19,6 +19,7 @@ export interface CurveSegmentState {
 }
 
 export interface CurveSegmentControls {
+    secStrucFirst: boolean, secStrucLast: boolean
     p0: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, p4: Vec3,
     d12: Vec3, d23: Vec3
 }
@@ -48,20 +49,25 @@ const curvePoint = Vec3.zero()
 
 export function interpolatePointsAndTangents(state: CurveSegmentState, controls: CurveSegmentControls, tension: number, shift: number) {
     const { curvePoints, tangentVectors, linearSegments } = state
-    const { p0, p1, p2, p3, p4 } = controls
+    const { p0, p1, p2, p3, p4, secStrucFirst, secStrucLast } = controls
 
     const shift1 = 1 - shift
 
+    const tensionBeg = secStrucFirst ? 0.5 : tension
+    const tensionEnd = secStrucLast ? 0.5 : tension
+
     for (let j = 0; j <= linearSegments; ++j) {
         const t = j * 1.0 / linearSegments;
         if (t < shift1) {
-            Vec3.spline(curvePoint, p0, p1, p2, p3, t + shift, tension)
-            Vec3.spline(tanA, p0, p1, p2, p3, t + shift + 0.01, tension)
-            Vec3.spline(tanB, p0, p1, p2, p3, t + shift - 0.01, tension)
+            const te = lerp(tensionBeg, tension, t)
+            Vec3.spline(curvePoint, p0, p1, p2, p3, t + shift, te)
+            Vec3.spline(tanA, p0, p1, p2, p3, t + shift + 0.01, tensionBeg)
+            Vec3.spline(tanB, p0, p1, p2, p3, t + shift - 0.01, tensionBeg)
         } else {
-            Vec3.spline(curvePoint, p1, p2, p3, p4, t - shift1, tension)
-            Vec3.spline(tanA, p1, p2, p3, p4, t - shift1 + 0.01, tension)
-            Vec3.spline(tanB, p1, p2, p3, p4, t - shift1 - 0.01, tension)
+            const te = lerp(tension, tensionEnd, t)
+            Vec3.spline(curvePoint, p1, p2, p3, p4, t - shift1, te)
+            Vec3.spline(tanA, p1, p2, p3, p4, t - shift1 + 0.01, te)
+            Vec3.spline(tanB, p1, p2, p3, p4, t - shift1 - 0.01, te)
         }
         Vec3.toArray(curvePoint, curvePoints, j * 3)
         Vec3.normalize(tangentVec, Vec3.sub(tangentVec, tanA, tanB))
@@ -69,17 +75,15 @@ export function interpolatePointsAndTangents(state: CurveSegmentState, controls:
     }
 }
 
-const tmpNormal = Vec3.zero()
-const tangentVec = Vec3.zero()
-const normalVec = Vec3.zero()
-const binormalVec = Vec3.zero()
-const prevNormal = Vec3.zero()
-const firstControlPoint = Vec3.zero()
-const lastControlPoint = Vec3.zero()
-const firstTangentVec = Vec3.zero()
-const lastTangentVec = Vec3.zero()
-const firstNormalVec = Vec3.zero()
-const lastNormalVec = Vec3.zero()
+const tmpNormal = Vec3()
+const tangentVec = Vec3()
+const normalVec = Vec3()
+const binormalVec = Vec3()
+const prevNormal = Vec3()
+const firstTangentVec = Vec3()
+const lastTangentVec = Vec3()
+const firstNormalVec = Vec3()
+const lastNormalVec = Vec3()
 
 /**
  * Populate normalVectors by interpolating from firstDirection to lastDirection with
@@ -91,13 +95,11 @@ export function interpolateNormals(state: CurveSegmentState, controls: CurveSegm
 
     const n = curvePoints.length / 3
 
-    Vec3.fromArray(firstControlPoint, curvePoints, 0)
-    Vec3.fromArray(lastControlPoint, curvePoints, (n - 1) * 3)
     Vec3.fromArray(firstTangentVec, tangentVectors, 0)
-    Vec3.fromArray(lastTangentVec, tangentVectors,  (n - 1) * 3)
+    Vec3.fromArray(lastTangentVec, tangentVectors, (n - 1) * 3)
 
-    Vec3.orthogonalize(firstNormalVec, firstTangentVec, Vec3.sub(tmpNormal, firstControlPoint, firstDirection))
-    Vec3.orthogonalize(lastNormalVec, lastTangentVec, Vec3.sub(tmpNormal, lastControlPoint, lastDirection))
+    Vec3.orthogonalize(firstNormalVec, firstTangentVec, firstDirection)
+    Vec3.orthogonalize(lastNormalVec, lastTangentVec, lastDirection)
 
     if (Vec3.dot(firstNormalVec, lastNormalVec) < 0) {
         Vec3.scale(lastNormalVec, lastNormalVec, -1)
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 c73107161..c510f7c3b 100644
--- a/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts
+++ b/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts
@@ -57,13 +57,16 @@ function createPolymerTraceElement (unit: Unit): PolymerTraceElement {
         moleculeType: MoleculeType.unknown,
         coarseBackboneFirst: false, coarseBackboneLast: false,
         isCoarseBackbone: false,
-        p0: Vec3.zero(), p1: Vec3.zero(), p2: Vec3.zero(), p3: Vec3.zero(), p4: Vec3.zero(),
-        d12: Vec3.create(1, 0, 0), d23: Vec3.create(1, 0, 0),
+        p0: Vec3(), p1: Vec3(), p2: Vec3(), p3: Vec3(), p4: Vec3(),
+        d12: Vec3(), d23: Vec3()
     }
 }
 
 const enum AtomicPolymerTraceIteratorState { nextPolymer, nextResidue }
 
+const tmpVecA = Vec3()
+const tmpVecB = Vec3()
+
 export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> {
     private value: PolymerTraceElement
     private polymerIt: SortedRanges.Iterator<ElementIndex, ResidueIndex>
@@ -83,22 +86,23 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
     private state: AtomicPolymerTraceIteratorState = AtomicPolymerTraceIteratorState.nextPolymer
     private residueAtomSegments: Segmentation<ElementIndex, ResidueIndex>
     private traceElementIndex: ArrayLike<ElementIndex>
-    private directionElementIndex: ArrayLike<ElementIndex | -1>
+    private directionFromElementIndex: ArrayLike<ElementIndex | -1>
+    private directionToElementIndex: ArrayLike<ElementIndex | -1>
     private moleculeType: ArrayLike<MoleculeType>
     private atomicConformation: AtomicConformation
 
-    private p0 = Vec3.zero();
-    private p1 = Vec3.zero();
-    private p2 = Vec3.zero();
-    private p3 = Vec3.zero();
-    private p4 = Vec3.zero();
-    private p5 = Vec3.zero();
-    private p6 = Vec3.zero();
+    private p0 = Vec3()
+    private p1 = Vec3()
+    private p2 = Vec3()
+    private p3 = Vec3()
+    private p4 = Vec3()
+    private p5 = Vec3()
+    private p6 = Vec3()
 
-    // private v01 = Vec3.zero();
-    private v12 = Vec3.create(1, 0, 0);
-    private v23 = Vec3.create(1, 0, 0);
-    // private v34 = Vec3.zero();
+    private d01 = Vec3()
+    private d12 = Vec3()
+    private d23 = Vec3()
+    private d34 = Vec3()
 
     hasNext: boolean = false;
 
@@ -148,6 +152,20 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
         }
     }
 
+    private setFromToVector(out: Vec3, residueIndex: ResidueIndex) {
+        this.pos(tmpVecA, this.directionFromElementIndex[residueIndex])
+        this.pos(tmpVecB, this.directionToElementIndex[residueIndex])
+        Vec3.sub(out, tmpVecB, tmpVecA)
+    }
+
+    private setDirection(out: Vec3, v1: Vec3, v2: Vec3, v3: Vec3) {
+        Vec3.copy(tmpVecA, v1)
+        Vec3.copy(tmpVecB, v3)
+        if (Vec3.dot(v2, tmpVecA) < 0) Vec3.scale(tmpVecA, tmpVecA, -1)
+        if (Vec3.dot(v2, tmpVecB) < 0) Vec3.scale(tmpVecB, tmpVecB, -1)
+        Vec3.scale(out, Vec3.add(out, tmpVecA, Vec3.add(out, tmpVecB, Vec3.add(out, v2, v2))), 1/4)
+    }
+
     move() {
         const { residueIt, polymerIt, value } = this
 
@@ -161,7 +179,7 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
                     this.currSecStrucType = SecStrucTypeNA
                     this.nextSecStrucType = this.getSecStruc(this.residueSegmentMin)
                     this.currCoarseBackbone = false
-                    this.nextCoarseBackbone = this.directionElementIndex[this.residueSegmentMin] === -1
+                    this.nextCoarseBackbone = this.directionFromElementIndex[this.residueSegmentMin] === -1 || this.directionToElementIndex[this.residueSegmentMin] === -1
                     break
                 }
             }
@@ -175,7 +193,7 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
 
             this.prevCoarseBackbone = this.currCoarseBackbone
             this.currCoarseBackbone = this.nextCoarseBackbone
-            this.nextCoarseBackbone = residueIt.hasNext ? this.directionElementIndex[residueIndex + 1] === -1 : false
+            this.nextCoarseBackbone = residueIt.hasNext ? (this.directionFromElementIndex[residueIndex + 1] === -1 || this.directionToElementIndex[residueIndex + 1] === -1) : false
 
             value.secStrucType = this.currSecStrucType
             value.secStrucFirst = this.prevSecStrucType !== this.currSecStrucType
@@ -205,7 +223,9 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
                 this.pos(this.p4, this.traceElementIndex[residueIndexNext1])
                 this.pos(this.p5, this.traceElementIndex[residueIndexNext2])
 
-                this.pos(this.v12, this.directionElementIndex[residueIndexPrev1])
+                this.setFromToVector(this.d01, residueIndexPrev1)
+                this.setFromToVector(this.d12, residueIndex)
+                this.setFromToVector(this.d23, residueIndexNext1)
             } else {
                 value.centerPrev.element = value.center.element
                 value.center.element = value.centerNext.element
@@ -217,12 +237,14 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
                 Vec3.copy(this.p4, this.p5)
                 Vec3.copy(this.p5, this.p6)
 
-                Vec3.copy(this.v12, this.v23)
+                Vec3.copy(this.d01, this.d12)
+                Vec3.copy(this.d12, this.d23)
+                Vec3.copy(this.d23, this.d34)
             }
             value.centerNext.element = this.traceElementIndex[residueIndexNext1]
-            this.pos(this.p6,  this.traceElementIndex[residueIndexNext3])
-            this.pos(this.v23, this.directionElementIndex[residueIndex])
-            value.isCoarseBackbone = this.directionElementIndex[residueIndex] === -1
+            this.pos(this.p6, this.traceElementIndex[residueIndexNext3])
+            this.setFromToVector(this.d34, residueIndexNext2)
+            value.isCoarseBackbone = this.directionFromElementIndex[residueIndex] === -1 || this.directionToElementIndex[residueIndex] === -1
 
             this.setControlPoint(value.p0, this.p0, this.p1, this.p2, residueIndexPrev2)
             this.setControlPoint(value.p1, this.p1, this.p2, this.p3, residueIndexPrev1)
@@ -230,8 +252,8 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
             this.setControlPoint(value.p3, this.p3, this.p4, this.p5, residueIndexNext1)
             this.setControlPoint(value.p4, this.p4, this.p5, this.p6, residueIndexNext2)
 
-            Vec3.copy(value.d12, this.v12)
-            Vec3.copy(value.d23, this.v23)
+            this.setDirection(value.d12, this.d01, this.d12, this.d23)
+            this.setDirection(value.d23, this.d12, this.d23, this.d34)
 
             if (!residueIt.hasNext) {
                 this.state = AtomicPolymerTraceIteratorState.nextPolymer
@@ -247,7 +269,8 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
         this.atomicConformation = unit.model.atomicConformation
         this.residueAtomSegments = unit.model.atomicHierarchy.residueAtomSegments
         this.traceElementIndex = unit.model.atomicHierarchy.derived.residue.traceElementIndex as ArrayLike<ElementIndex> // can assume it won't be -1 for polymer residues
-        this.directionElementIndex = unit.model.atomicHierarchy.derived.residue.directionElementIndex
+        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.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements)
-- 
GitLab