Ver Fonte

wip, computed secondary structure prop

Alexander Rose há 6 anos atrás
pai
commit
0c6d924e95

+ 3 - 3
src/mol-model-formats/structure/mmcif/secondary-structure.ts

@@ -11,12 +11,12 @@ import { AtomicHierarchy, AtomicConformation } from 'mol-model/structure/model/p
 import { SecondaryStructure } from 'mol-model/structure/model/properties/seconday-structure';
 import { Column } from 'mol-data/db';
 import { ChainIndex, ResidueIndex } from 'mol-model/structure/model/indexing';
-import { computeSecondaryStructure } from 'mol-model/structure/model/properties/utils/secondary-structure';
+import { computeModelDSSP } from 'mol-model-props/computed/secondary-structure/dssp';
 
 // TODO add parameter to allow forcing computation
 export function getSecondaryStructure(data: mmCIF_Database, hierarchy: AtomicHierarchy, conformation: AtomicConformation): SecondaryStructure {
     if (!data.struct_conf._rowCount && !data.struct_sheet_range._rowCount) {
-        return computeSecondaryStructure(hierarchy, conformation)
+        return computeModelDSSP(hierarchy, conformation)
     } else {
         return getSecondaryStructureMmCif(data, hierarchy)
     }
@@ -36,7 +36,7 @@ export function getSecondaryStructureMmCif(data: mmCIF_Database, hierarchy: Atom
     };
 
     if (map.size > 0) assignSecondaryStructureRanges(hierarchy, map, secStruct);
-    return secStruct;
+    return SecondaryStructure(secStruct.type, secStruct.key, secStruct.elements);
 }
 
 type SecondaryStructureEntry = {

+ 21 - 19
src/mol-model-props/computed/secondary-structure.ts

@@ -6,21 +6,24 @@
 
 import { CustomPropertyDescriptor, Structure } from 'mol-model/structure';
 import { Task } from 'mol-task';
+import { DSSPComputationParams, computeModelDSSP } from './secondary-structure/dssp';
+import { SecondaryStructure } from 'mol-model/structure/model/properties/seconday-structure';
+import { ParamDefinition as PD } from 'mol-util/param-definition';
 
 export namespace ComputedSecondaryStructure {
-    export type Property = {} // TODO
+    export type Property = SecondaryStructure
 
     export function get(structure: Structure): Property | undefined {
-        return structure.currentPropertyData.__ComputedSecondaryStructure__;
+        return structure.inheritedPropertyData.__ComputedSecondaryStructure__;
     }
     function set(structure: Structure, prop: Property) {
-        (structure.currentPropertyData.__ComputedSecondaryStructure__ as Property) = prop;
+        (structure.inheritedPropertyData.__ComputedSecondaryStructure__ as Property) = prop;
     }
 
-    export function createAttachTask() {
+    export function createAttachTask(params: Partial<SecondaryStructureComputationProps> = {}) {
         return (structure: Structure) => Task.create('Compute Secondary Structure', async ctx => {
             if (get(structure)) return true;
-            return await attachFromCifOrCompute(structure, ctx)
+            return await attachFromCifOrCompute(structure, params)
         });
     }
 
@@ -30,12 +33,10 @@ export namespace ComputedSecondaryStructure {
         // TODO `cifExport` and `symbol`
     });
 
-    export async function attachFromCifOrCompute(structure: Structure, params: {
-        // TODO params
-    }) {
+    export async function attachFromCifOrCompute(structure: Structure, params: Partial<SecondaryStructureComputationProps> = {}) {
         if (structure.customPropertyDescriptors.has(Descriptor)) return true;
 
-        const compSecStruc = computeSecondaryStructure(structure)
+        const compSecStruc = computeSecondaryStructure(structure, params)
 
         structure.customPropertyDescriptors.add(Descriptor);
         set(structure, compSecStruc);
@@ -43,14 +44,15 @@ export namespace ComputedSecondaryStructure {
     }
 }
 
-// export const SecondaryStructureComputationParams = {
-//     oldDefinition: PD.Boolean(true, { description: 'Whether to use the old DSSP convention for the annotation of turns and helices, causes them to be two residues shorter' }),
-//     oldOrdering: PD.Boolean(true, { description: 'Alpha-helices are preferred over 3-10 helices' })
-// }
-// export type SecondaryStructureComputationParams = typeof SecondaryStructureComputationParams
-
-function computeSecondaryStructure(structure: Structure): ComputedSecondaryStructure.Property {
-    // TODO
-    console.log('TODO calc secondary structure')
-    return {}
+export const SecondaryStructureComputationParams = {
+    ...DSSPComputationParams
+}
+export type SecondaryStructureComputationParams = typeof SecondaryStructureComputationParams
+export type SecondaryStructureComputationProps = PD.Values<SecondaryStructureComputationParams>
+
+function computeSecondaryStructure(structure: Structure, params: Partial<SecondaryStructureComputationProps>): ComputedSecondaryStructure.Property {
+    // TODO compute from structure not from model
+    // TODO use Zhang-Skolnik for CA alpha only parts or for coarse parts with per-residue elements
+    const { atomicHierarchy, atomicConformation } = structure.model
+    return computeModelDSSP(atomicHierarchy, atomicConformation, params)
 }

+ 8 - 19
src/mol-model/structure/model/properties/utils/secondary-structure.ts → src/mol-model-props/computed/secondary-structure/dssp.ts

@@ -14,9 +14,9 @@ import { SortedArray } from 'mol-data/int';
 import { IntAdjacencyGraph } from 'mol-math/graph';
 import { BitFlags } from 'mol-util';
 import { ElementIndex } from 'mol-model/structure/model/indexing';
-import { AtomicHierarchy, AtomicConformation } from '../atomic';
 import { ParamDefinition as PD } from 'mol-util/param-definition'
 import { radToDeg } from 'mol-math/misc';
+import { AtomicHierarchy, AtomicConformation } from 'mol-model/structure/model/properties/atomic';
 
 /**
  * TODO bugs to fix:
@@ -47,7 +47,7 @@ const hbondEnergyCutoff = -0.5
 const hbondEnergyMinimal = -9.9
 
 interface DSSPContext {
-    params: Partial<PD.Values<SecondaryStructureComputationParams>>,
+    params: Partial<PD.Values<DSSPComputationParams>>,
     getResidueFlag: (f: DSSPType) => SecondaryStructureType,
     getFlagName: (f: DSSPType) => String,
 
@@ -113,22 +113,17 @@ namespace DSSPType {
     }
 }
 
-export const SecondaryStructureComputationParams = {
+export const DSSPComputationParams = {
     oldDefinition: PD.Boolean(true, { description: 'Whether to use the old DSSP convention for the annotation of turns and helices, causes them to be two residues shorter' }),
     oldOrdering: PD.Boolean(true, { description: 'Alpha-helices are preferred over 3-10 helices' })
 }
-export type SecondaryStructureComputationParams = typeof SecondaryStructureComputationParams
-
-export function computeSecondaryStructure(hierarchy: AtomicHierarchy,
-    conformation: AtomicConformation) {
-    // TODO use Zhang-Skolnik for CA alpha only parts or for coarse parts with per-residue elements
-    return computeModelDSSP(hierarchy, conformation)
-}
+export type DSSPComputationParams = typeof DSSPComputationParams
+export type DSSPComputationProps = PD.Values<DSSPComputationParams>
 
 export function computeModelDSSP(hierarchy: AtomicHierarchy,
     conformation: AtomicConformation,
-    params: Partial<PD.Values<SecondaryStructureComputationParams>> = {}): SecondaryStructure {
-    params = { ...PD.getDefaultValues(SecondaryStructureComputationParams), ...params };
+    params: Partial<DSSPComputationProps> = {}): SecondaryStructure {
+    params = { ...PD.getDefaultValues(DSSPComputationParams), ...params };
 
     const { lookup3d, proteinResidues } = calcAtomicTraceLookup3D(hierarchy, conformation)
     const backboneIndices = calcBackboneAtomIndices(hierarchy, proteinResidues)
@@ -187,13 +182,7 @@ export function computeModelDSSP(hierarchy: AtomicHierarchy,
         keys[i] = elements.length - 1
     }
 
-    const secondaryStructure: SecondaryStructure = {
-        type,
-        key: keys,
-        elements: elements
-    }
-
-    return secondaryStructure
+    return SecondaryStructure(type, keys, elements)
 }
 
 function createElement(kind: string, flag: DSSPType.Flag, getResidueFlag: (f: DSSPType) => SecondaryStructureType): SecondaryStructure.Element {

+ 0 - 0
src/mol-model/structure/model/properties/utils/secondary-structure.validation → src/mol-model-props/computed/secondary-structure/dssp.validation


+ 1 - 0
src/mol-model-props/computed/secondary-structure/zhang-skolnik.ts

@@ -0,0 +1 @@
+// TODO

+ 8 - 1
src/mol-model/structure/model/properties/seconday-structure.ts

@@ -5,17 +5,24 @@
  */
 
 import { SecondaryStructureType } from '../types';
+import { idFactory } from 'mol-util/id-factory';
+
+const getNextSecondaryStructureId = idFactory()
 
 /** Secondary structure "indexed" by residues. */
 interface SecondaryStructure {
+    readonly id: number
     readonly type: ArrayLike<SecondaryStructureType>,
-
     /** index into the elements array */
     readonly key: ArrayLike<number>,
     /** indexed by key */
     readonly elements: ReadonlyArray<SecondaryStructure.Element>
 }
 
+function SecondaryStructure(type: SecondaryStructure['type'], key: SecondaryStructure['key'], elements: SecondaryStructure['elements']) {
+    return { id: getNextSecondaryStructureId(), type, key, elements }
+}
+
 namespace SecondaryStructure {
     export type Element = None | Turn | Helix | Sheet
 

+ 1 - 0
src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts

@@ -53,6 +53,7 @@ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: bo
         }
 
         unregister() {
+            console.log('unregister')
             this.ctx.customModelProperties.unregister(StructureQualityReport.Descriptor.name);
             this.ctx.lociLabels.removeProvider(labelPDBeValidation);
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove('pdbe-structure-quality-report')

+ 7 - 2
src/mol-repr/structure/complex-visual.ts

@@ -106,8 +106,13 @@ export function ComplexVisual<G extends Geometry, P extends ComplexParams & Geom
             updateState.createGeometry = true
         }
 
-        if (!ColorTheme.areEqual(theme.color, currentTheme.color)) updateState.updateColor = true
-        if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true
+        if (!ColorTheme.areEqual(theme.color, currentTheme.color)) {
+            updateState.updateColor = true
+        }
+
+        if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) {
+            updateState.createGeometry = true
+        }
 
         if (updateState.createGeometry) {
             updateState.updateColor = true

+ 21 - 18
src/mol-repr/structure/units-visual.ts

@@ -53,7 +53,7 @@ interface UnitsVisualBuilder<P extends UnitsParams, G extends Geometry> {
     createLocationIterator(group: Unit.SymmetryGroup): LocationIterator
     getLoci(pickingId: PickingId, structureGroup: StructureGroup, id: number): Loci
     eachLocation(loci: Loci, structureGroup: StructureGroup, apply: (interval: Interval) => boolean): boolean
-    setUpdateState(state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme): void
+    setUpdateState(state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme, newStructureGroup: StructureGroup, currentStructureGroup: StructureGroup): void
 }
 
 interface UnitsVisualGeometryBuilder<P extends UnitsParams, G extends Geometry> extends UnitsVisualBuilder<P, G> {
@@ -100,12 +100,13 @@ export function UnitsVisual<G extends Geometry, P extends UnitsParams & Geometry
             return
         }
 
-        setUpdateState(updateState, newProps, currentProps, theme, currentTheme)
+        setUpdateState(updateState, newProps, currentProps, newTheme, currentTheme, newStructureGroup, currentStructureGroup)
 
-        if (!ColorTheme.areEqual(theme.color, currentTheme.color)) {
+        if (!ColorTheme.areEqual(newTheme.color, currentTheme.color)) {
             // console.log('new colorTheme')
             updateState.updateColor = true
         }
+
         if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) {
             // console.log('new unitKinds')
             updateState.createGeometry = true
@@ -121,10 +122,12 @@ export function UnitsVisual<G extends Geometry, P extends UnitsParams & Geometry
         }
 
         // check if the conformation of unit.model has changed
-        // if (Unit.conformationId(newStructureGroup.group.units[0]) !== Unit.conformationId(currentStructureGroup.group.units[0])) {
-        if (Unit.conformationId(newStructureGroup.group.units[0]) !== Unit.conformationId(currentStructureGroup.group.units[0])
+        const newUnit = newStructureGroup.group.units[0]
+        const currentUnit = currentStructureGroup.group.units[0]
+        // if (Unit.conformationId(newUnit) !== Unit.conformationId(currentUnit)) {
+        if (Unit.conformationId(newUnit) !== Unit.conformationId(currentUnit)
             // TODO: this needs more attention
-            || newStructureGroup.group.units[0].conformation !== currentStructureGroup.group.units[0].conformation) {
+            || newUnit.conformation !== currentUnit.conformation) {
             // console.log('new conformation')
             updateState.updateTransform = true;
             updateState.createGeometry = true
@@ -270,8 +273,8 @@ export interface UnitsMeshVisualBuilder<P extends UnitsMeshParams> extends Units
 export function UnitsMeshVisual<P extends UnitsMeshParams>(builder: UnitsMeshVisualBuilder<P>, materialId: number): UnitsVisual<P> {
     return UnitsVisual<Mesh, StructureMeshParams & UnitsParams>({
         ...builder,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
-            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme, newStructureGroup: StructureGroup, currentStructureGroup: StructureGroup) => {
+            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme, newStructureGroup, currentStructureGroup)
             if (!SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true
         },
         geometryUtils: Mesh.Utils
@@ -287,8 +290,8 @@ export interface UnitsSpheresVisualBuilder<P extends UnitsSpheresParams> extends
 export function UnitsSpheresVisual<P extends UnitsSpheresParams>(builder: UnitsSpheresVisualBuilder<P>, materialId: number): UnitsVisual<P> {
     return UnitsVisual<Spheres, StructureSpheresParams & UnitsParams>({
         ...builder,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
-            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme, newStructureGroup: StructureGroup, currentStructureGroup: StructureGroup) => {
+            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme, newStructureGroup, currentStructureGroup)
             if (!SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.updateSize = true
         },
         geometryUtils: Spheres.Utils
@@ -304,8 +307,8 @@ export interface UnitsPointVisualBuilder<P extends UnitsPointsParams> extends Un
 export function UnitsPointsVisual<P extends UnitsPointsParams>(builder: UnitsPointVisualBuilder<P>, materialId: number): UnitsVisual<P> {
     return UnitsVisual<Points, StructurePointsParams & UnitsParams>({
         ...builder,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
-            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme, newStructureGroup: StructureGroup, currentStructureGroup: StructureGroup) => {
+            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme, newStructureGroup, currentStructureGroup)
             if (!SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.updateSize = true
         },
         geometryUtils: Points.Utils
@@ -321,8 +324,8 @@ export interface UnitsLinesVisualBuilder<P extends UnitsLinesParams> extends Uni
 export function UnitsLinesVisual<P extends UnitsLinesParams>(builder: UnitsLinesVisualBuilder<P>, materialId: number): UnitsVisual<P> {
     return UnitsVisual<Lines, StructureLinesParams & UnitsParams>({
         ...builder,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
-            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme, newStructureGroup: StructureGroup, currentStructureGroup: StructureGroup) => {
+            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme, newStructureGroup, currentStructureGroup)
             if (!SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.updateSize = true
         },
         geometryUtils: Lines.Utils
@@ -338,8 +341,8 @@ export interface UnitsDirectVolumeVisualBuilder<P extends UnitsDirectVolumeParam
 export function UnitsDirectVolumeVisual<P extends UnitsDirectVolumeParams>(builder: UnitsDirectVolumeVisualBuilder<P>, materialId: number): UnitsVisual<P> {
     return UnitsVisual<DirectVolume, StructureDirectVolumeParams & UnitsParams>({
         ...builder,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
-            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme, newStructureGroup: StructureGroup, currentStructureGroup: StructureGroup) => {
+            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme, newStructureGroup, currentStructureGroup)
             if (!SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true
         },
         geometryUtils: DirectVolume.Utils
@@ -355,8 +358,8 @@ export interface UnitsTextureMeshVisualBuilder<P extends UnitsTextureMeshParams>
 export function UnitsTextureMeshVisual<P extends UnitsTextureMeshParams>(builder: UnitsTextureMeshVisualBuilder<P>, materialId: number): UnitsVisual<P> {
     return UnitsVisual<TextureMesh, StructureTextureMeshParams & UnitsParams>({
         ...builder,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
-            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme, newStructureGroup: StructureGroup, currentStructureGroup: StructureGroup) => {
+            builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme, newStructureGroup, currentStructureGroup)
             if (!SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true
         },
         geometryUtils: TextureMesh.Utils

+ 1 - 1
src/mol-repr/structure/visual/polymer-direction-wedge.ts

@@ -50,7 +50,7 @@ function createPolymerDirectionWedgeMesh(ctx: VisualContext, unit: Unit, structu
     const { normalVectors, binormalVectors } = state
 
     let i = 0
-    const polymerTraceIt = PolymerTraceIterator(unit)
+    const polymerTraceIt = PolymerTraceIterator(unit, structure)
     while (polymerTraceIt.hasNext) {
         const v = polymerTraceIt.move()
         builderState.currentGroup = i

+ 12 - 4
src/mol-repr/structure/visual/polymer-trace-mesh.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>
  */
@@ -9,7 +9,7 @@ import { UnitsVisual } from '../representation';
 import { VisualUpdateState } from '../../util';
 import { PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment, PolymerLocationIterator, getPolymerElementLoci, eachPolymerElement, interpolateSizes } from './util/polymer';
 import { SecondaryStructureType, isNucleic } from 'mol-model/structure/model/types';
-import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual';
+import { UnitsMeshVisual, UnitsMeshParams, StructureGroup } from '../units-visual';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
@@ -17,6 +17,8 @@ import { addSheet } from 'mol-geo/geometry/mesh/builder/sheet';
 import { addTube } from 'mol-geo/geometry/mesh/builder/tube';
 import { VisualContext } from 'mol-repr/visual';
 import { Theme } from 'mol-theme/theme';
+import { ComputedSecondaryStructure } from 'mol-model-props/computed/secondary-structure';
+import { SecondaryStructure } from 'mol-model/structure/model/properties/seconday-structure';
 
 export const PolymerTraceMeshParams = {
     sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }),
@@ -44,7 +46,7 @@ function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: Struc
     const { curvePoints, normalVectors, binormalVectors, widthValues, heightValues } = state
 
     let i = 0
-    const polymerTraceIt = PolymerTraceIterator(unit)
+    const polymerTraceIt = PolymerTraceIterator(unit, structure)
     while (polymerTraceIt.hasNext) {
         const v = polymerTraceIt.move()
         builderState.currentGroup = i
@@ -111,7 +113,7 @@ export function PolymerTraceVisual(materialId: number): UnitsVisual<PolymerTrace
         createLocationIterator: PolymerLocationIterator.fromGroup,
         getLoci: getPolymerElementLoci,
         eachLocation: eachPolymerElement,
-        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<PolymerTraceParams>, currentProps: PD.Values<PolymerTraceParams>) => {
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<PolymerTraceParams>, currentProps: PD.Values<PolymerTraceParams>, newTheme: Theme, currentTheme: Theme, newStructureGroup: StructureGroup, currentStructureGroup: StructureGroup) => {
             state.createGeometry = (
                 newProps.sizeFactor !== currentProps.sizeFactor ||
                 newProps.linearSegments !== currentProps.linearSegments ||
@@ -119,6 +121,12 @@ export function PolymerTraceVisual(materialId: number): UnitsVisual<PolymerTrace
                 newProps.aspectRatio !== currentProps.aspectRatio ||
                 newProps.arrowFactor !== currentProps.arrowFactor
             )
+
+            const computedSecondaryStructure = ComputedSecondaryStructure.get(newStructureGroup.structure)
+            if ((state.info.computedSecondaryStructure as SecondaryStructure) !== computedSecondaryStructure) {
+                state.createGeometry = true;
+                state.info.computedSecondaryStructure = computedSecondaryStructure
+            }
         }
     }, materialId)
 }

+ 1 - 1
src/mol-repr/structure/visual/polymer-tube-mesh.ts

@@ -40,7 +40,7 @@ function createPolymerTubeMesh(ctx: VisualContext, unit: Unit, structure: Struct
     const { curvePoints, normalVectors, binormalVectors, widthValues, heightValues } = state
 
     let i = 0
-    const polymerTraceIt = PolymerTraceIterator(unit)
+    const polymerTraceIt = PolymerTraceIterator(unit, structure)
     while (polymerTraceIt.hasNext) {
         const v = polymerTraceIt.move()
         builderState.currentGroup = i

+ 10 - 7
src/mol-repr/structure/visual/util/polymer/trace-iterator.ts

@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Unit, StructureElement, ElementIndex, ResidueIndex } from 'mol-model/structure';
+import { Unit, StructureElement, ElementIndex, ResidueIndex, Structure } from 'mol-model/structure';
 import { Segmentation } from 'mol-data/int';
 import { MoleculeType, SecondaryStructureType } from 'mol-model/structure/model/types';
 import Iterator from 'mol-data/iterator';
@@ -13,17 +13,18 @@ import SortedRanges from 'mol-data/int/sorted-ranges';
 import { CoarseSphereConformation, CoarseGaussianConformation } from 'mol-model/structure/model/properties/coarse';
 import { getPolymerRanges } from '../polymer';
 import { AtomicConformation } from 'mol-model/structure/model/properties/atomic';
+import { ComputedSecondaryStructure } from 'mol-model-props/computed/secondary-structure';
 
 /**
  * Iterates over individual residues/coarse elements in polymers of a unit while
  * providing information about the neighbourhood in the underlying model for drawing splines
  */
-export function PolymerTraceIterator(unit: Unit): Iterator<PolymerTraceElement> {
+export function PolymerTraceIterator(unit: Unit, structure: Structure): Iterator<PolymerTraceElement> {
     switch (unit.kind) {
-        case Unit.Kind.Atomic: return new AtomicPolymerTraceIterator(unit)
+        case Unit.Kind.Atomic: return new AtomicPolymerTraceIterator(unit, structure)
         case Unit.Kind.Spheres:
         case Unit.Kind.Gaussians:
-            return new CoarsePolymerTraceIterator(unit)
+            return new CoarsePolymerTraceIterator(unit, structure)
     }
 }
 
@@ -236,18 +237,20 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
         return this.value;
     }
 
-    constructor(private unit: Unit.Atomic) {
+    constructor(private unit: Unit.Atomic, structure: Structure) {
         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.moleculeType = unit.model.atomicHierarchy.derived.residue.moleculeType
         this.cyclicPolymerMap = unit.model.atomicHierarchy.cyclicPolymerMap
-        this.secondaryStructureType = unit.model.properties.secondaryStructure.type
         this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements)
         this.residueIt = Segmentation.transientSegments(this.residueAtomSegments, unit.elements);
         this.value = createPolymerTraceElement(unit)
         this.hasNext = this.residueIt.hasNext && this.polymerIt.hasNext
+
+        const computedSecondaryStructure = ComputedSecondaryStructure.get(structure)
+        this.secondaryStructureType = (computedSecondaryStructure && computedSecondaryStructure.type) || unit.model.properties.secondaryStructure.type
     }
 }
 
@@ -316,7 +319,7 @@ export class CoarsePolymerTraceIterator implements Iterator<PolymerTraceElement>
         return this.value;
     }
 
-    constructor(private unit: Unit.Spheres | Unit.Gaussians) {
+    constructor(private unit: Unit.Spheres | Unit.Gaussians, structure: Structure) {
         this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements);
         this.value = createPolymerTraceElement(unit)
         switch (unit.kind) {

+ 5 - 0
src/mol-repr/util.ts

@@ -15,6 +15,9 @@ export interface VisualUpdateState {
     updateSize: boolean
     createGeometry: boolean
     createNew: boolean
+
+    /** holds contextual info, is not reset  */
+    info: { [k: string]: unknown }
 }
 export namespace VisualUpdateState {
     export function create(): VisualUpdateState {
@@ -25,6 +28,8 @@ export namespace VisualUpdateState {
             updateSize: false,
             createGeometry: false,
             createNew: false,
+
+            info: {}
         }
     }
     export function reset(state: VisualUpdateState) {

+ 8 - 2
src/mol-theme/color.ts

@@ -36,6 +36,7 @@ interface ColorTheme<P extends PD.Params> {
     readonly granularity: ColorType
     readonly color: LocationColor
     readonly props: Readonly<PD.Values<P>>
+    readonly contextHash?: number
     readonly description?: string
     readonly legend?: Readonly<ScaleLegend | TableLegend>
 }
@@ -44,10 +45,15 @@ namespace ColorTheme {
     export type Factory<P extends PD.Params> = (ctx: ThemeDataContext, props: PD.Values<P>) => ColorTheme<P>
     export const EmptyFactory = () => Empty
     const EmptyColor = Color(0xCCCCCC)
-    export const Empty: ColorTheme<{}> = { factory: EmptyFactory, granularity: 'uniform', color: () => EmptyColor, props: {} }
+    export const Empty: ColorTheme<{}> = {
+        factory: EmptyFactory,
+        granularity: 'uniform',
+        color: () => EmptyColor,
+        props: {}
+    }
 
     export function areEqual(themeA: ColorTheme<any>, themeB: ColorTheme<any>) {
-        return themeA.factory === themeB.factory && deepEqual(themeA.props, themeB.props)
+        return themeA.contextHash === themeB.contextHash && themeA.factory === themeB.factory && deepEqual(themeA.props, themeB.props)
     }
 
     export interface Provider<P extends PD.Params> extends ThemeProvider<ColorTheme<P>, P> { }

+ 14 - 4
src/mol-theme/color/secondary-structure.ts

@@ -13,6 +13,8 @@ import { getElementMoleculeType } from 'mol-model/structure/util';
 import { ParamDefinition as PD } from 'mol-util/param-definition'
 import { ThemeDataContext } from '../theme';
 import { TableLegend } from 'mol-util/color/tables';
+import { ComputedSecondaryStructure } from 'mol-model-props/computed/secondary-structure';
+import { SecondaryStructure } from 'mol-model/structure/model/properties/seconday-structure';
 
 // from Jmol http://jmol.sourceforge.net/jscolors/ (shapely)
 const SecondaryStructureColors = ColorMap({
@@ -40,10 +42,12 @@ export function getSecondaryStructureColorThemeParams(ctx: ThemeDataContext) {
     return SecondaryStructureColorThemeParams // TODO return copy
 }
 
-export function secondaryStructureColor(unit: Unit, element: ElementIndex): Color {
+export function secondaryStructureColor(unit: Unit, element: ElementIndex, computedSecondaryStructure: SecondaryStructure | undefined): Color {
     let secStrucType = SecondaryStructureType.create(SecondaryStructureType.Flag.None)
     if (Unit.isAtomic(unit)) {
-        secStrucType = unit.model.properties.secondaryStructure.type[unit.residueIndex[element]]
+        secStrucType = computedSecondaryStructure ?
+            computedSecondaryStructure.type[unit.residueIndex[element]] :
+            unit.model.properties.secondaryStructure.type[unit.residueIndex[element]]
     }
 
     if (SecondaryStructureType.is(secStrucType, SecondaryStructureType.Flag.Helix)) {
@@ -75,11 +79,16 @@ export function secondaryStructureColor(unit: Unit, element: ElementIndex): Colo
 }
 
 export function SecondaryStructureColorTheme(ctx: ThemeDataContext, props: PD.Values<SecondaryStructureColorThemeParams>): ColorTheme<SecondaryStructureColorThemeParams> {
+
+    const computedSecondaryStructure = ctx.structure && ComputedSecondaryStructure.get(ctx.structure)
+    // use `computedSecondaryStructure.id` as context hash, when available
+    const contextHash = computedSecondaryStructure && computedSecondaryStructure.id
+
     function color(location: Location): Color {
         if (StructureElement.isLocation(location)) {
-            return secondaryStructureColor(location.unit, location.element)
+            return secondaryStructureColor(location.unit, location.element, computedSecondaryStructure)
         } else if (Link.isLocation(location)) {
-            return secondaryStructureColor(location.aUnit, location.aUnit.elements[location.aIndex])
+            return secondaryStructureColor(location.aUnit, location.aUnit.elements[location.aIndex], computedSecondaryStructure)
         }
         return DefaultSecondaryStructureColor
     }
@@ -89,6 +98,7 @@ export function SecondaryStructureColorTheme(ctx: ThemeDataContext, props: PD.Va
         granularity: 'group',
         color,
         props,
+        contextHash,
         description: Description,
         legend: TableLegend(Object.keys(SecondaryStructureColors).map(name => {
             return [name, (SecondaryStructureColors as any)[name] as Color] as [string, Color]

+ 1 - 1
src/tests/browser/render-structure.ts

@@ -12,10 +12,10 @@ import { ColorTheme } from 'mol-theme/color';
 import { SizeTheme } from 'mol-theme/size';
 import { CartoonRepresentationProvider } from 'mol-repr/structure/representation/cartoon';
 import { trajectoryFromMmCIF } from 'mol-model-formats/structure/mmcif';
-import { computeModelDSSP } from 'mol-model/structure/model/properties/utils/secondary-structure';
 import { MolecularSurfaceRepresentationProvider } from 'mol-repr/structure/representation/molecular-surface';
 import { BallAndStickRepresentationProvider } from 'mol-repr/structure/representation/ball-and-stick';
 import { GaussianSurfaceRepresentationProvider } from 'mol-repr/structure/representation/gaussian-surface';
+import { computeModelDSSP } from 'mol-model-props/computed/secondary-structure/dssp';
 
 const parent = document.getElementById('app')!
 parent.style.width = '100%'