Переглянути джерело

basic support for interpolated size of tube polymer traces

Alexander Rose 6 роки тому
батько
коміт
8fed9a4526

+ 10 - 1
src/mol-geo/geometry/mesh/builder/tube.ts

@@ -27,7 +27,7 @@ function add3AndScale2(out: Vec3, a: Vec3, b: Vec3, c: Vec3, sa: number, sb: num
     out[2] = (a[2] * sa) + (b[2] * sb) + c[2];
 }
 
-export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, radialSegments: number, width: number, height: number, waveFactor: number, startCap: boolean, endCap: boolean) {
+export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, radialSegments: number, widthValues: ArrayLike<number>, heightValues: ArrayLike<number>, waveFactor: number, startCap: boolean, endCap: boolean) {
     const { currentGroup, vertices, normals, indices, groups } = state
 
     let vertexCount = vertices.elementCount
@@ -39,6 +39,9 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe
         Vec3.fromArray(v, binormalVectors, i3)
         Vec3.fromArray(controlPoint, controlPoints, i3)
 
+        const width = widthValues[i]
+        const height = heightValues[i]
+
         const tt = di * i - 0.5;
         const ff = 1 + (waveFactor - 1) * (Math.cos(2 * Math.PI * tt) + 1);
         const w = ff * width, h = ff * height;
@@ -83,6 +86,9 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe
         ChunkedArray.add3(vertices, controlPoint[0], controlPoint[1], controlPoint[2]);
         ChunkedArray.add3(normals, normalVector[0], normalVector[1], normalVector[2]);
 
+        const width = widthValues[0]
+        const height = heightValues[0]
+
         vertexCount = vertices.elementCount
         for (let i = 0; i < radialSegments; ++i) {
             const t = 2 * Math.PI * i / radialSegments;
@@ -112,6 +118,9 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe
         ChunkedArray.add3(vertices, controlPoint[0], controlPoint[1], controlPoint[2]);
         ChunkedArray.add3(normals, normalVector[0], normalVector[1], normalVector[2]);
 
+        const width = widthValues[linearSegments]
+        const height = heightValues[linearSegments]
+
         vertexCount = vertices.elementCount
         for (let i = 0; i < radialSegments; ++i) {
             const t = 2 * Math.PI * i / radialSegments

+ 28 - 13
src/mol-repr/structure/visual/polymer-trace-mesh.ts

@@ -7,7 +7,7 @@
 import { Unit, Structure } from 'mol-model/structure';
 import { UnitsVisual } from '../representation';
 import { VisualUpdateState } from '../../util';
-import { PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment, PolymerLocationIterator, getPolymerElementLoci, markPolymerElement } from './util/polymer';
+import { PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment, PolymerLocationIterator, getPolymerElementLoci, markPolymerElement, interpolateSizes } from './util/polymer';
 import { SecondaryStructureType, isNucleic } from 'mol-model/structure/model/types';
 import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
@@ -41,7 +41,7 @@ function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: Struc
 
     const isCoarse = Unit.isCoarse(unit)
     const state = createCurveSegmentState(linearSegments)
-    const { curvePoints, normalVectors, binormalVectors } = state
+    const { curvePoints, normalVectors, binormalVectors, widthValues, heightValues } = state
 
     let i = 0
     const polymerTraceIt = PolymerTraceIterator(unit)
@@ -57,24 +57,39 @@ function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: Struc
 
         interpolateCurveSegment(state, v, tension, shift)
 
-        let width = theme.size.size(v.center) * sizeFactor
-        if (isCoarse) width *= aspectRatio / 2
+        let w0 = theme.size.size(v.centerPrev) * sizeFactor
+        let w1 = theme.size.size(v.center) * sizeFactor
+        let w2 = theme.size.size(v.centerNext) * sizeFactor
+        if (isCoarse) {
+            w0 *= aspectRatio / 2
+            w1 *= aspectRatio / 2
+            w2 *= aspectRatio / 2
+        }
 
         if (isSheet) {
-            const height = width * aspectRatio
-            const arrowHeight = v.secStrucLast ? height * arrowFactor : 0
-            addSheet(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, width, height, arrowHeight, v.secStrucFirst, v.secStrucLast)
+            const h1 = w1 * aspectRatio
+            const arrowHeight = v.secStrucLast ? h1 * arrowFactor : 0
+            addSheet(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, w1, h1, arrowHeight, v.secStrucFirst, v.secStrucLast)
         } else {
-            let height: number
+            let h0: number, h1: number, h2: number
             if (isHelix) {
-                height = width * aspectRatio
+                h0 = w0 * aspectRatio
+                h1 = w1 * aspectRatio
+                h2 = w2 * aspectRatio
             } else if (isNucleicType) {
-                height = width * aspectRatio;
-                [width, height] = [height, width]
+                h0 = w0 * aspectRatio;
+                [w0, h0] = [h0, w0]
+                h1 = w1 * aspectRatio;
+                [w1, h1] = [h1, w1]
+                h2 = w2 * aspectRatio;
+                [w2, h2] = [h2, w2]
             } else {
-                height = width
+                h0 = w0
+                h1 = w1
+                h2 = w2
             }
-            addTube(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, radialSegments, width, height, 1, v.secStrucFirst, v.secStrucLast)
+            interpolateSizes(state, w0, w1, w2, h0, h1, h2, shift)
+            addTube(builderState, curvePoints, normalVectors, binormalVectors, linearSegments, radialSegments, widthValues, heightValues, 1, v.secStrucFirst, v.secStrucLast)
         }
 
         ++i

+ 24 - 1
src/mol-repr/structure/visual/util/polymer/curve-segment.ts

@@ -6,12 +6,15 @@
 
 import { Vec3 } from 'mol-math/linear-algebra';
 import { NumberArray } from 'mol-util/type-helpers';
+import { lerp } from 'mol-math/interpolate';
 
 export interface CurveSegmentState {
     curvePoints: NumberArray,
     tangentVectors: NumberArray,
     normalVectors: NumberArray,
     binormalVectors: NumberArray,
+    widthValues: NumberArray,
+    heightValues: NumberArray,
     linearSegments: number
 }
 
@@ -21,12 +24,15 @@ export interface CurveSegmentControls {
 }
 
 export function createCurveSegmentState(linearSegments: number): CurveSegmentState {
-    const pn = (linearSegments + 1) * 3
+    const n = linearSegments + 1
+    const pn = n * 3
     return {
         curvePoints: new Float32Array(pn),
         tangentVectors: new Float32Array(pn),
         normalVectors: new Float32Array(pn),
         binormalVectors: new Float32Array(pn),
+        widthValues: new Float32Array(n),
+        heightValues: new Float32Array(n),
         linearSegments
     }
 }
@@ -112,4 +118,21 @@ export function interpolateNormals(state: CurveSegmentState, controls: CurveSegm
         Vec3.normalize(binormalVec, Vec3.cross(binormalVec, tangentVec, normalVec))
         Vec3.toArray(binormalVec, binormalVectors, i * 3)
     }
+}
+
+export function interpolateSizes(state: CurveSegmentState, w0: number, w1: number, w2: number, h0: number, h1: number, h2: number, shift: number) {
+    const { widthValues, heightValues, linearSegments } = state
+
+    const shift1 = 1 - shift
+
+    for (let i = 0; i <= linearSegments; ++i) {
+        const t = i * 1.0 / linearSegments;
+        if (t < shift1) {
+            widthValues[i] = lerp(w0, w1, t + shift)
+            heightValues[i] = lerp(h0, h1, t + shift)
+        } else {
+            widthValues[i] = lerp(w1, w2, t - shift1)
+            heightValues[i] = lerp(h1, h2, t - shift1)
+        }
+    }
 }

+ 47 - 19
src/mol-repr/structure/visual/util/polymer/trace-iterator.ts

@@ -29,6 +29,8 @@ export function PolymerTraceIterator(unit: Unit): Iterator<PolymerTraceElement>
 
 interface PolymerTraceElement {
     center: StructureElement
+    centerPrev: StructureElement
+    centerNext: StructureElement
     first: boolean, last: boolean
     secStrucFirst: boolean, secStrucLast: boolean
     secStrucType: SecondaryStructureType
@@ -43,6 +45,8 @@ const SecStrucTypeNA = SecondaryStructureType.create(SecondaryStructureType.Flag
 function createPolymerTraceElement (unit: Unit): PolymerTraceElement {
     return {
         center: StructureElement.create(unit),
+        centerPrev: StructureElement.create(unit),
+        centerNext: StructureElement.create(unit),
         first: false, last: false,
         secStrucFirst: false, secStrucLast: false,
         secStrucType: SecStrucTypeNA,
@@ -154,23 +158,35 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
             this.nextSecStrucType = residueIt.hasNext ? this.secondaryStructureType[residueIndex + 1] : SecStrucTypeNA
 
             value.secStrucType = this.currSecStrucType
-            value.center.element = this.traceElementIndex[residueIndex]
             value.first = residueIndex === this.residueSegmentMin
             value.last = residueIndex === this.residueSegmentMax
             value.secStrucFirst = this.prevSecStrucType !== this.currSecStrucType
             value.secStrucLast = this.currSecStrucType !== this.nextSecStrucType
             value.moleculeType = this.moleculeType[residueIndex]
 
+            const residueIndexPrev3 = this.getResidueIndex(residueIndex - 3)
+            const residueIndexPrev2 = this.getResidueIndex(residueIndex - 2)
+            const residueIndexPrev1 = this.getResidueIndex(residueIndex - 1)
+            const residueIndexNext1 = this.getResidueIndex(residueIndex + 1)
+            const residueIndexNext2 = this.getResidueIndex(residueIndex + 2)
+            const residueIndexNext3 = this.getResidueIndex(residueIndex + 3)
+
             if (value.first) {
-                this.pos(this.p0, this.traceElementIndex[this.getResidueIndex(residueIndex - 3)])
-                this.pos(this.p1, this.traceElementIndex[this.getResidueIndex(residueIndex - 2)])
-                this.pos(this.p2, this.traceElementIndex[this.getResidueIndex(residueIndex - 1)])
-                this.pos(this.p3, value.center.element)
-                this.pos(this.p4, this.traceElementIndex[this.getResidueIndex(residueIndex + 1)])
-                this.pos(this.p5, this.traceElementIndex[this.getResidueIndex(residueIndex + 2)])
-
-                this.pos(this.v12, this.directionElementIndex[this.getResidueIndex(residueIndex - 1)])
+                value.centerPrev.element = this.traceElementIndex[residueIndexPrev1]
+                value.center.element = this.traceElementIndex[residueIndex]
+
+                this.pos(this.p0, this.traceElementIndex[residueIndexPrev3])
+                this.pos(this.p1, this.traceElementIndex[residueIndexPrev2])
+                this.pos(this.p2, this.traceElementIndex[residueIndexPrev1])
+                this.pos(this.p3, this.traceElementIndex[residueIndex])
+                this.pos(this.p4, this.traceElementIndex[residueIndexNext1])
+                this.pos(this.p5, this.traceElementIndex[residueIndexNext2])
+
+                this.pos(this.v12, this.directionElementIndex[residueIndexPrev1])
             } else {
+                value.centerPrev.element = value.center.element
+                value.center.element = value.centerNext.element
+
                 Vec3.copy(this.p0, this.p1)
                 Vec3.copy(this.p1, this.p2)
                 Vec3.copy(this.p2, this.p3)
@@ -180,14 +196,15 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
 
                 Vec3.copy(this.v12, this.v23)
             }
-            this.pos(this.p6,  this.traceElementIndex[this.getResidueIndex(residueIndex + 3 as ResidueIndex)])
+            value.centerNext.element = this.traceElementIndex[residueIndexNext1]
+            this.pos(this.p6,  this.traceElementIndex[residueIndexNext3])
             this.pos(this.v23, this.directionElementIndex[residueIndex])
 
-            this.setControlPoint(value.p0, this.p0, this.p1, this.p2, residueIndex - 2 as ResidueIndex)
-            this.setControlPoint(value.p1, this.p1, this.p2, this.p3, residueIndex - 1 as ResidueIndex)
+            this.setControlPoint(value.p0, this.p0, this.p1, this.p2, residueIndexPrev2)
+            this.setControlPoint(value.p1, this.p1, this.p2, this.p3, residueIndexPrev1)
             this.setControlPoint(value.p2, this.p2, this.p3, this.p4, residueIndex)
-            this.setControlPoint(value.p3, this.p3, this.p4, this.p5, residueIndex + 1 as ResidueIndex)
-            this.setControlPoint(value.p4, this.p4, this.p5, this.p6, residueIndex + 2 as ResidueIndex)
+            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)
@@ -228,8 +245,11 @@ export class CoarsePolymerTraceIterator implements Iterator<PolymerTraceElement>
     private elementIndex: number
     hasNext: boolean = false;
 
+    private getElementIndex(elementIndex: number) {
+        return Math.min(Math.max(this.polymerSegment.start, elementIndex), this.polymerSegment.end - 1) as ElementIndex
+    }
+
     private pos(target: Vec3, elementIndex: number) {
-        elementIndex = Math.min(Math.max(this.polymerSegment.start, elementIndex), this.polymerSegment.end - 1)
         const index = this.unit.elements[elementIndex]
         target[0] = this.conformation.x[index]
         target[1] = this.conformation.y[index]
@@ -251,13 +271,21 @@ export class CoarsePolymerTraceIterator implements Iterator<PolymerTraceElement>
 
         if (this.state === CoarsePolymerTraceIteratorState.nextElement) {
             this.elementIndex += 1
+
+            const elementIndexPrev2 = this.getElementIndex(this.elementIndex - 2)
+            const elementIndexPrev1 = this.getElementIndex(this.elementIndex - 1)
+            const elementIndexNext1 = this.getElementIndex(this.elementIndex + 1)
+            const elementIndexNext2 = this.getElementIndex(this.elementIndex + 2)
+
+            this.value.centerPrev.element = this.value.center.unit.elements[elementIndexPrev1]
             this.value.center.element = this.value.center.unit.elements[this.elementIndex]
+            this.value.centerNext.element = this.value.center.unit.elements[elementIndexNext1]
 
-            this.pos(this.value.p0, this.elementIndex - 2)
-            this.pos(this.value.p1, this.elementIndex - 1)
+            this.pos(this.value.p0, elementIndexPrev2)
+            this.pos(this.value.p1, elementIndexPrev1)
             this.pos(this.value.p2, this.elementIndex)
-            this.pos(this.value.p3, this.elementIndex + 1)
-            this.pos(this.value.p4, this.elementIndex + 2)
+            this.pos(this.value.p3, elementIndexNext1)
+            this.pos(this.value.p4, elementIndexNext2)
 
             this.value.first = this.elementIndex === this.polymerSegment.start
             this.value.last = this.elementIndex === this.polymerSegment.end - 1