Ver Fonte

refactored curve interpolation, moved direction wedges into own visual

Alexander Rose há 6 anos atrás
pai
commit
f1a8f9e0ca

+ 13 - 4
src/mol-geo/representation/structure/cartoon.ts

@@ -13,11 +13,13 @@ import { MarkerAction } from '../../util/marker-data';
 import { PolymerTraceVisual, DefaultPolymerTraceProps } from './visual/polymer-trace-mesh';
 import { PolymerGapVisual, DefaultPolymerGapProps } from './visual/polymer-gap-cylinder';
 import { NucleotideBlockVisual, DefaultNucleotideBlockProps } from './visual/nucleotide-block-mesh';
+import { PolymerDirectionVisual, DefaultPolymerDirectionProps } from './visual/polymer-direction-wedge';
 
 export const DefaultCartoonProps = {
     ...DefaultPolymerTraceProps,
     ...DefaultPolymerGapProps,
-    ...DefaultNucleotideBlockProps
+    ...DefaultNucleotideBlockProps,
+    ...DefaultPolymerDirectionProps
 }
 export type CartoonProps = Partial<typeof DefaultCartoonProps>
 
@@ -25,13 +27,14 @@ export function CartoonRepresentation(): StructureRepresentation<CartoonProps> {
     const traceRepr = StructureUnitsRepresentation(PolymerTraceVisual)
     const gapRepr = StructureUnitsRepresentation(PolymerGapVisual)
     const blockRepr = StructureUnitsRepresentation(NucleotideBlockVisual)
+    const directionRepr = StructureUnitsRepresentation(PolymerDirectionVisual)
 
     return {
         get renderObjects() {
-            return [ ...traceRepr.renderObjects, ...gapRepr.renderObjects, ...blockRepr.renderObjects ]
+            return [ ...traceRepr.renderObjects, ...gapRepr.renderObjects, ...blockRepr.renderObjects, ...directionRepr.renderObjects ]
         },
         get props() {
-            return { ...traceRepr.props, ...gapRepr.props, ...blockRepr.props }
+            return { ...traceRepr.props, ...gapRepr.props, ...blockRepr.props, ...directionRepr.props }
         },
         create: (structure: Structure, props: CartoonProps = {} as CartoonProps) => {
             const p = Object.assign({}, DefaultCartoonProps, props)
@@ -39,6 +42,7 @@ export function CartoonRepresentation(): StructureRepresentation<CartoonProps> {
                 await traceRepr.create(structure, p).runInContext(ctx)
                 await gapRepr.create(structure, p).runInContext(ctx)
                 await blockRepr.create(structure, p).runInContext(ctx)
+                await directionRepr.create(structure, p).runInContext(ctx)
             })
         },
         update: (props: CartoonProps) => {
@@ -47,25 +51,30 @@ export function CartoonRepresentation(): StructureRepresentation<CartoonProps> {
                 await traceRepr.update(p).runInContext(ctx)
                 await gapRepr.update(p).runInContext(ctx)
                 await blockRepr.update(p).runInContext(ctx)
+                await directionRepr.update(p).runInContext(ctx)
             })
         },
         getLoci: (pickingId: PickingId) => {
             const traceLoci = traceRepr.getLoci(pickingId)
             const gapLoci = gapRepr.getLoci(pickingId)
             const blockLoci = blockRepr.getLoci(pickingId)
+            const directionLoci = directionRepr.getLoci(pickingId)
             return !isEmptyLoci(traceLoci) ? traceLoci
                 : !isEmptyLoci(gapLoci) ? gapLoci
-                : blockLoci
+                : !isEmptyLoci(blockLoci) ? blockLoci
+                : directionLoci
         },
         mark: (loci: Loci, action: MarkerAction) => {
             traceRepr.mark(loci, action)
             gapRepr.mark(loci, action)
             blockRepr.mark(loci, action)
+            directionRepr.mark(loci, action)
         },
         destroy() {
             traceRepr.destroy()
             gapRepr.destroy()
             blockRepr.destroy()
+            directionRepr.destroy()
         }
     }
 }

+ 176 - 0
src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts

@@ -0,0 +1,176 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { ValueCell } from 'mol-util/value-cell'
+
+import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
+import { Unit, StructureElement } from 'mol-model/structure';
+import { DefaultStructureProps, UnitsVisual } from '..';
+import { RuntimeContext } from 'mol-task'
+import { createTransforms, createColors } from './util/common';
+import { markElement } from './util/element';
+import { deepEqual } from 'mol-util';
+import { MeshValues } from 'mol-gl/renderable';
+import { getMeshData } from '../../../util/mesh-data';
+import { Mesh } from '../../../shape/mesh';
+import { PickingId } from '../../../util/picking';
+import { OrderedSet } from 'mol-data/int';
+import { createMarkers, MarkerAction } from '../../../util/marker-data';
+import { Loci, EmptyLoci } from 'mol-model/loci';
+import { SizeTheme } from '../../../theme';
+import { createMeshValues, updateMeshValues, updateRenderableState, createRenderableState, DefaultMeshProps } from '../../util';
+import { MeshBuilder } from '../../../shape/mesh-builder';
+import { getPolymerElementCount, PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment } from './util/polymer';
+import { Vec3, Mat4 } from 'mol-math/linear-algebra';
+import { SecondaryStructureType, MoleculeType } from 'mol-model/structure/model/types';
+
+const t = Mat4.identity()
+
+async function createPolymerDirectionWedgeMesh(ctx: RuntimeContext, unit: Unit, mesh?: Mesh) {
+    const polymerElementCount = getPolymerElementCount(unit)
+    console.log('polymerElementCount direction', polymerElementCount)
+    if (!polymerElementCount) return Mesh.createEmpty(mesh)
+
+    // TODO better vertex count estimates
+    const builder = MeshBuilder.create(polymerElementCount * 30, polymerElementCount * 30 / 2, mesh)
+    const linearSegments = 1
+
+    const state = createCurveSegmentState(linearSegments)
+    const { normalVectors, binormalVectors } = state
+
+    let i = 0
+    const polymerTraceIt = PolymerTraceIterator(unit)
+    while (polymerTraceIt.hasNext) {
+        const v = polymerTraceIt.move()
+        builder.setId(v.center.element)
+
+        const isNucleic = v.moleculeType === MoleculeType.DNA || v.moleculeType === MoleculeType.RNA
+        const isSheet = SecondaryStructureType.is(v.secStrucType, SecondaryStructureType.Flag.Beta)
+        const tension = (isNucleic || isSheet) ? 0.5 : 0.9
+
+        // console.log('ELEMENT', i)
+        interpolateCurveSegment(state, v, tension)
+
+        if ((isSheet && !v.secStrucChange) || !isSheet) {
+            const upVec = Vec3.zero()
+            let width = 0.5, height = 1.2, depth = 0.6
+            if (isNucleic) {
+                Vec3.fromArray(upVec, binormalVectors, Math.round(linearSegments / 2) * 3)
+                depth = 0.9
+            } else {
+                Vec3.fromArray(upVec, normalVectors, Math.round(linearSegments / 2) * 3)
+            }
+
+            Mat4.targetTo(t, v.p3, v.p1, upVec)
+            Mat4.mul(t, t, Mat4.rotXY90)
+            Mat4.setTranslation(t, v.p2)
+            builder.addWedge(t, { width, height, depth })
+        }
+
+        if (i % 10000 === 0 && ctx.shouldUpdate) {
+            await ctx.update({ message: 'Polymer direction mesh', current: i, max: polymerElementCount });
+        }
+        ++i
+    }
+
+    return builder.getMesh()
+}
+
+export const DefaultPolymerDirectionProps = {
+    ...DefaultMeshProps,
+    ...DefaultStructureProps,
+    sizeTheme: { name: 'physical', factor: 1 } as SizeTheme,
+    detail: 0,
+    unitKinds: [ Unit.Kind.Atomic, Unit.Kind.Spheres ] as Unit.Kind[]
+}
+export type PolymerDirectionProps = Partial<typeof DefaultPolymerDirectionProps>
+
+export function PolymerDirectionVisual(): UnitsVisual<PolymerDirectionProps> {
+    let renderObject: MeshRenderObject
+    let currentProps: typeof DefaultPolymerDirectionProps
+    let mesh: Mesh
+    let currentGroup: Unit.SymmetryGroup
+
+    return {
+        get renderObject () { return renderObject },
+        async create(ctx: RuntimeContext, group: Unit.SymmetryGroup, props: PolymerDirectionProps = {}) {
+            currentProps = Object.assign({}, DefaultPolymerDirectionProps, props)
+            currentGroup = group
+
+            const { colorTheme, unitKinds } = { ...DefaultPolymerDirectionProps, ...props }
+            const instanceCount = group.units.length
+            const elementCount = group.elements.length
+            const unit = group.units[0]
+
+            mesh = unitKinds.includes(unit.kind)
+                ? await createPolymerDirectionWedgeMesh(ctx, unit, mesh)
+                : Mesh.createEmpty(mesh)
+
+            const transforms = createTransforms(group)
+            const color = createColors(group, elementCount, colorTheme)
+            const marker = createMarkers(instanceCount * elementCount)
+
+            const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
+
+            const values: MeshValues = {
+                ...getMeshData(mesh),
+                ...color,
+                ...marker,
+                aTransform: transforms,
+                elements: mesh.indexBuffer,
+                ...createMeshValues(currentProps, counts),
+            }
+            const state = createRenderableState(currentProps)
+
+            renderObject = createMeshRenderObject(values, state)
+        },
+        async update(ctx: RuntimeContext, props: PolymerDirectionProps) {
+            const newProps = Object.assign({}, currentProps, props)
+
+            if (!renderObject) return false
+
+            let updateColor = false
+
+            if (newProps.detail !== currentProps.detail) {
+                const unit = currentGroup.units[0]
+                mesh = await createPolymerDirectionWedgeMesh(ctx, unit, mesh)
+                ValueCell.update(renderObject.values.drawCount, mesh.triangleCount * 3)
+                updateColor = true
+            }
+
+            if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) {
+                updateColor = true
+            }
+
+            if (updateColor) {
+                const elementCount = currentGroup.elements.length
+                if (ctx.shouldUpdate) await ctx.update('Computing direction colors');
+                createColors(currentGroup, elementCount, newProps.colorTheme, renderObject.values)
+            }
+
+            updateMeshValues(renderObject.values, newProps)
+            updateRenderableState(renderObject.state, newProps)
+
+            currentProps = newProps
+            return true
+        },
+        getLoci(pickingId: PickingId) {
+            const { objectId, instanceId, elementId } = pickingId
+            if (renderObject.id === objectId) {
+                const unit = currentGroup.units[instanceId]
+                const indices = OrderedSet.ofSingleton(elementId as StructureElement.UnitIndex);
+                return StructureElement.Loci([{ unit, indices }])
+            }
+            return EmptyLoci
+        },
+        mark(loci: Loci, action: MarkerAction) {
+            markElement(renderObject.values.tMarker, currentGroup, loci, action)
+        },
+        destroy() {
+            // TODO
+        }
+    }
+}

+ 6 - 55
src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts

@@ -23,16 +23,10 @@ import { Loci, EmptyLoci } from 'mol-model/loci';
 import { SizeTheme } from '../../../theme';
 import { createMeshValues, updateMeshValues, updateRenderableState, createRenderableState, DefaultMeshProps } from '../../util';
 import { MeshBuilder } from '../../../shape/mesh-builder';
-import { getPolymerElementCount, PolymerTraceIterator, interpolateNormals } from './util/polymer';
-import { Vec3, Mat4 } from 'mol-math/linear-algebra';
+import { getPolymerElementCount, PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment } from './util/polymer';
 import { SecondaryStructureType, MoleculeType } from 'mol-model/structure/model/types';
 
 // TODO handle polymer ends properly
-// TODO avoid allocating Vec3, use global temp vars
-// TODO move more interpolation code into ./util/polymer/interpolate
-// TODO move direction wedges into separate visual
-
-const t = Mat4.identity()
 
 async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, mesh?: Mesh) {
     const polymerElementCount = getPolymerElementCount(unit)
@@ -44,17 +38,8 @@ async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, mesh?: Me
     const linearSegments = 8
     const radialSegments = 12
 
-    const tanA = Vec3.zero()
-    const tanB = Vec3.zero()
-
-    const tB = Vec3.zero()
-    const tangentVec = Vec3.zero()
-
-    const pn = (linearSegments + 1) * 3
-    const controlPoints = new Float32Array(pn)
-    const tangentVectors = new Float32Array(pn)
-    const normalVectors = new Float32Array(pn)
-    const binormalVectors = new Float32Array(pn)
+    const state = createCurveSegmentState(linearSegments)
+    const { curvePoints, normalVectors, binormalVectors } = state
 
     let i = 0
     const polymerTraceIt = PolymerTraceIterator(unit)
@@ -67,26 +52,8 @@ async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, mesh?: Me
         const isHelix = SecondaryStructureType.is(v.secStrucType, SecondaryStructureType.Flag.Helix)
         const tension = (isNucleic || isSheet) ? 0.5 : 0.9
 
-        for (let j = 0; j <= linearSegments; ++j) {
-            const t = j * 1.0 / linearSegments;
-            // if ((v.last && t > 0.5) || (v.first && t < 0.5)) break
-
-            if (t < 0.5) {
-                Vec3.spline(tB, v.t0, v.t1, v.t2, v.t3, t + 0.5, tension)
-                Vec3.spline(tanA, v.t0, v.t1, v.t2, v.t3, t + 0.5 + 0.01, tension)
-                Vec3.spline(tanB, v.t0, v.t1, v.t2, v.t3, t + 0.5 - 0.01, tension)
-            } else {
-                Vec3.spline(tB, v.t1, v.t2, v.t3, v.t4, t - 0.5, tension)
-                Vec3.spline(tanA, v.t1, v.t2, v.t3, v.t4, t - 0.5 + 0.01, tension)
-                Vec3.spline(tanB, v.t1, v.t2, v.t3, v.t4, t - 0.5 - 0.01, tension)
-            }
-            Vec3.toArray(tB, controlPoints, j * 3)
-            Vec3.normalize(tangentVec, Vec3.sub(tangentVec, tanA, tanB))
-            Vec3.toArray(tangentVec, tangentVectors, j * 3)
-        }
-
         // console.log('ELEMENT', i)
-        interpolateNormals(controlPoints, tangentVectors, normalVectors, binormalVectors, v.d12, v.d23)
+        interpolateCurveSegment(state, v, tension)
 
         let width = 0.2, height = 0.2
 
@@ -94,30 +61,14 @@ async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, mesh?: Me
         if (isSheet) {
             width = 0.15; height = 1.0
             const arrowHeight = v.secStrucChange ? 1.7 : 0
-            builder.addSheet(controlPoints, normalVectors, binormalVectors, linearSegments, width, height, arrowHeight, true, true)
+            builder.addSheet(curvePoints, normalVectors, binormalVectors, linearSegments, width, height, arrowHeight, true, true)
         } else {
             if (isHelix) {
                 width = 0.2; height = 1.0
             } else if (isNucleic) {
                 width = 1.5; height = 0.3
             }
-            builder.addTube(controlPoints, normalVectors, binormalVectors, linearSegments, radialSegments, width, height, 1, true, true)
-        }
-
-        if ((isSheet && !v.secStrucChange) || !isSheet) {
-            const upVec = Vec3.zero()
-            let width = 0.5, height = 1.2, depth = 0.6
-            if (isNucleic) {
-                Vec3.fromArray(upVec, binormalVectors, Math.round(linearSegments / 2) * 3)
-                depth = 0.9
-            } else {
-                Vec3.fromArray(upVec, normalVectors, Math.round(linearSegments / 2) * 3)
-            }
-
-            Mat4.targetTo(t, v.t3, v.t1, upVec)
-            Mat4.mul(t, t, Mat4.rotXY90)
-            Mat4.setTranslation(t, v.t2)
-            builder.addWedge(t, { width, height, depth })
+            builder.addTube(curvePoints, normalVectors, binormalVectors, linearSegments, radialSegments, width, height, 1, true, true)
         }
 
         if (i % 10000 === 0 && ctx.shouldUpdate) {

+ 1 - 1
src/mol-geo/representation/structure/visual/util/polymer.ts

@@ -11,7 +11,7 @@ import SortedRanges from 'mol-data/int/sorted-ranges';
 export * from './polymer/backbone-iterator'
 export * from './polymer/gap-iterator'
 export * from './polymer/trace-iterator'
-export * from './polymer/interpolate'
+export * from './polymer/curve-segment'
 
 export function getPolymerRanges(unit: Unit): SortedRanges<ElementIndex> {
     switch (unit.kind) {

+ 118 - 0
src/mol-geo/representation/structure/visual/util/polymer/curve-segment.ts

@@ -0,0 +1,118 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Vec3 } from 'mol-math/linear-algebra';
+
+export interface CurveSegmentState {
+    curvePoints: Helpers.NumberArray,
+    tangentVectors: Helpers.NumberArray,
+    normalVectors: Helpers.NumberArray,
+    binormalVectors: Helpers.NumberArray,
+    linearSegments: number
+}
+
+export interface CurveSegmentControls {
+    p0: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, p4: Vec3,
+    d12: Vec3, d23: Vec3
+}
+
+export function createCurveSegmentState(linearSegments: number): CurveSegmentState {
+    const pn = (linearSegments + 1) * 3
+    return {
+        curvePoints: new Float32Array(pn),
+        tangentVectors: new Float32Array(pn),
+        normalVectors: new Float32Array(pn),
+        binormalVectors: new Float32Array(pn),
+        linearSegments
+    }
+}
+
+export function interpolateCurveSegment(state: CurveSegmentState, controls: CurveSegmentControls, tension: number) {
+    interpolatePointsAndTangents(state, controls, tension)
+    interpolateNormals(state, controls)
+}
+
+const tanA = Vec3.zero()
+const tanB = Vec3.zero()
+const tB = Vec3.zero()
+
+export function interpolatePointsAndTangents(state: CurveSegmentState, controls: CurveSegmentControls, tension: number) {
+    const { curvePoints, tangentVectors, linearSegments } = state
+    const { p0, p1, p2, p3, p4 } = controls
+
+    for (let j = 0; j <= linearSegments; ++j) {
+        const t = j * 1.0 / linearSegments;
+        // if ((v.last && t > 0.5) || (v.first && t < 0.5)) break
+
+        if (t < 0.5) {
+            Vec3.spline(tB, p0, p1, p2, p3, t + 0.5, tension)
+            Vec3.spline(tanA, p0, p1, p2, p3, t + 0.5 + 0.01, tension)
+            Vec3.spline(tanB, p0, p1, p2, p3, t + 0.5 - 0.01, tension)
+        } else {
+            Vec3.spline(tB, p1, p2, p3, p4, t - 0.5, tension)
+            Vec3.spline(tanA, p1, p2, p3, p4, t - 0.5 + 0.01, tension)
+            Vec3.spline(tanB, p1, p2, p3, p4, t - 0.5 - 0.01, tension)
+        }
+        Vec3.toArray(tB, curvePoints, j * 3)
+        Vec3.normalize(tangentVec, Vec3.sub(tangentVec, tanA, tanB))
+        Vec3.toArray(tangentVec, tangentVectors, j * 3)
+    }
+}
+
+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()
+
+/**
+ * Populate normalVectors by interpolating from firstDirection to lastDirection with
+ * resulting vector perpendicular to tangentVectors and binormalVectors
+ */
+export function interpolateNormals(state: CurveSegmentState, controls: CurveSegmentControls) {
+    const { curvePoints, tangentVectors, normalVectors, binormalVectors } = state
+    const { d12: firstDirection, d23: lastDirection } = controls
+
+    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.normalize(tmpNormal, Vec3.sub(tmpNormal, firstControlPoint, firstDirection))
+    Vec3.orthogonalize(firstNormalVec, firstTangentVec, tmpNormal)
+
+    Vec3.normalize(tmpNormal, Vec3.sub(tmpNormal, lastControlPoint, lastDirection))
+    Vec3.orthogonalize(lastNormalVec, lastTangentVec, tmpNormal)
+
+    if (Vec3.dot(firstNormalVec, lastNormalVec) < 0) {
+        Vec3.scale(lastNormalVec, lastNormalVec, -1)
+    }
+
+    Vec3.copy(prevNormal, firstNormalVec)
+
+    for (let i = 0; i < n; ++i) {
+        const t = i === 0 ? 0 : 1 / (n - i)
+        Vec3.normalize(tmpNormal, Vec3.slerp(tmpNormal, prevNormal, lastNormalVec, t))
+
+        Vec3.fromArray(tangentVec, tangentVectors, i * 3)
+
+        Vec3.orthogonalize(normalVec, tangentVec, tmpNormal)
+        Vec3.toArray(normalVec, normalVectors, i * 3)
+
+        Vec3.copy(prevNormal, normalVec)
+
+        Vec3.normalize(binormalVec, Vec3.cross(binormalVec, tangentVec, normalVec))
+        Vec3.toArray(binormalVec, binormalVectors, i * 3)
+    }
+}

+ 0 - 59
src/mol-geo/representation/structure/visual/util/polymer/interpolate.ts

@@ -1,59 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
-
-import { Vec3 } from 'mol-math/linear-algebra';
-
-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()
-
-/**
- * Populate normalVectors by interpolating from firstDirection to lastDirection with
- * resulting vector perpendicular to tangentVectors and binormalVectors
- */
-export function interpolateNormals(controlPoints: Helpers.NumberArray, tangentVectors: Helpers.NumberArray, normalVectors: Helpers.NumberArray, binormalVectors: Helpers.NumberArray, firstDirection: Vec3, lastDirection: Vec3) {
-    const n = controlPoints.length / 3
-
-    Vec3.fromArray(firstControlPoint, controlPoints, 0)
-    Vec3.fromArray(lastControlPoint, controlPoints, (n - 1) * 3)
-    Vec3.fromArray(firstTangentVec, tangentVectors, 0)
-    Vec3.fromArray(lastTangentVec, tangentVectors,  (n - 1) * 3)
-
-    Vec3.normalize(tmpNormal, Vec3.sub(tmpNormal, firstControlPoint, firstDirection))
-    Vec3.orthogonalize(firstNormalVec, firstTangentVec, tmpNormal)
-
-    Vec3.normalize(tmpNormal, Vec3.sub(tmpNormal, lastControlPoint, lastDirection))
-    Vec3.orthogonalize(lastNormalVec, lastTangentVec, tmpNormal)
-
-    if (Vec3.dot(firstNormalVec, lastNormalVec) < 0) {
-        Vec3.scale(lastNormalVec, lastNormalVec, -1)
-    }
-
-    Vec3.copy(prevNormal, firstNormalVec)
-
-    for (let i = 0; i < n; ++i) {
-        const t = i === 0 ? 0 : 1 / (n - i)
-        Vec3.normalize(tmpNormal, Vec3.slerp(tmpNormal, prevNormal, lastNormalVec, t))
-
-        Vec3.fromArray(tangentVec, tangentVectors, i * 3)
-
-        Vec3.orthogonalize(normalVec, tangentVec, tmpNormal)
-        Vec3.toArray(normalVec, normalVectors, i * 3)
-
-        Vec3.copy(prevNormal, normalVec)
-
-        Vec3.normalize(binormalVec, Vec3.cross(binormalVec, tangentVec, normalVec))
-        Vec3.toArray(binormalVec, binormalVectors, i * 3)
-    }
-}

+ 13 - 12
src/mol-geo/representation/structure/visual/util/polymer/trace-iterator.ts

@@ -33,7 +33,8 @@ interface PolymerTraceElement {
     secStrucType: SecondaryStructureType
     secStrucChange: boolean
     moleculeType: MoleculeType
-    t0: Vec3, t1: Vec3, t2: Vec3, t3: Vec3, t4: Vec3
+
+    p0: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, p4: Vec3
     d12: Vec3, d23: Vec3
 }
 
@@ -44,7 +45,7 @@ function createPolymerTraceElement (unit: Unit): PolymerTraceElement {
         secStrucType: SecondaryStructureType.create(SecondaryStructureType.Flag.NA),
         secStrucChange: false,
         moleculeType: MoleculeType.unknown,
-        t0: Vec3.zero(), t1: Vec3.zero(), t2: Vec3.zero(), t3: Vec3.zero(), t4: Vec3.zero(),
+        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),
     }
 }
@@ -162,11 +163,11 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
 
             this.value.secStrucType = this.unit.model.properties.secondaryStructure.type[residueIndex]
 
-            this.setControlPoint(value.t0, this.p0, this.p1, this.p2, residueIndex - 2 as ResidueIndex)
-            this.setControlPoint(value.t1, this.p1, this.p2, this.p3, residueIndex - 1 as ResidueIndex)
-            this.setControlPoint(value.t2, this.p2, this.p3, this.p4, residueIndex)
-            this.setControlPoint(value.t3, this.p3, this.p4, this.p5, residueIndex + 1 as ResidueIndex)
-            this.setControlPoint(value.t4, this.p4, this.p5, this.p6, residueIndex + 2 as 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.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)
 
             Vec3.copy(value.d12, this.v12)
             Vec3.copy(value.d23, this.v23)
@@ -231,11 +232,11 @@ export class CoarsePolymerTraceIterator implements Iterator<PolymerTraceElement>
             this.elementIndex += 1
             this.value.center.element = this.value.center.unit.elements[this.elementIndex]
 
-            this.pos(this.value.t0, this.elementIndex - 2)
-            this.pos(this.value.t1, this.elementIndex - 1)
-            this.pos(this.value.t2, this.elementIndex)
-            this.pos(this.value.t3, this.elementIndex + 1)
-            this.pos(this.value.t4, this.elementIndex + 2)
+            this.pos(this.value.p0, this.elementIndex - 2)
+            this.pos(this.value.p1, this.elementIndex - 1)
+            this.pos(this.value.p2, this.elementIndex)
+            this.pos(this.value.p3, this.elementIndex + 1)
+            this.pos(this.value.p4, this.elementIndex + 2)
 
             this.value.first = this.elementIndex === this.polymerSegment.start
             this.value.last = this.elementIndex === this.polymerSegment.end - 1

+ 1 - 0
src/mol-view/stage.ts

@@ -72,6 +72,7 @@ export class Stage {
         this.ctx.viewer = this.viewer
 
         // this.loadPdbid('1jj2')
+        // this.loadPdbid('1grm') // helix-like sheets
         // this.loadPdbid('4umt') // ligand has bond with order 3
         // this.loadPdbid('1crn') // small
         // this.loadPdbid('1hrv') // viral assembly