diff --git a/src/mol-model-formats/structure/mmcif/secondary-structure.ts b/src/mol-model-formats/structure/mmcif/secondary-structure.ts index 0ba80546f6aa6d92d472d0a541f4dd7aed16671b..5d83fad54b63c7200e1cb8278d3edb11ec0beb3a 100644 --- a/src/mol-model-formats/structure/mmcif/secondary-structure.ts +++ b/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 = { diff --git a/src/mol-model-props/computed/secondary-structure.ts b/src/mol-model-props/computed/secondary-structure.ts index 82cb45c68cf5b62276e691445e90003f58385490..48fe6ee221b06f4a29e9b35e032c9736328fc03a 100644 --- a/src/mol-model-props/computed/secondary-structure.ts +++ b/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) } \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/utils/secondary-structure.ts b/src/mol-model-props/computed/secondary-structure/dssp.ts similarity index 97% rename from src/mol-model/structure/model/properties/utils/secondary-structure.ts rename to src/mol-model-props/computed/secondary-structure/dssp.ts index 7dcd761b8942c66e2815f857dd1401524b68e64b..af16b2814453134390c499866da863af5b7dad33 100644 --- a/src/mol-model/structure/model/properties/utils/secondary-structure.ts +++ b/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 { diff --git a/src/mol-model/structure/model/properties/utils/secondary-structure.validation b/src/mol-model-props/computed/secondary-structure/dssp.validation similarity index 100% rename from src/mol-model/structure/model/properties/utils/secondary-structure.validation rename to src/mol-model-props/computed/secondary-structure/dssp.validation diff --git a/src/mol-model-props/computed/secondary-structure/zhang-skolnik.ts b/src/mol-model-props/computed/secondary-structure/zhang-skolnik.ts new file mode 100644 index 0000000000000000000000000000000000000000..0ffdd02fcbce683e436c0030ffe0517135c6ceda --- /dev/null +++ b/src/mol-model-props/computed/secondary-structure/zhang-skolnik.ts @@ -0,0 +1 @@ +// TODO \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/seconday-structure.ts b/src/mol-model/structure/model/properties/seconday-structure.ts index 9434507ebc9ae1a11b2fc02f4cb9e00d0d4c4d0c..ce78f5589acaf7356d46469e9dc2f063a14550e9 100644 --- a/src/mol-model/structure/model/properties/seconday-structure.ts +++ b/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 diff --git a/src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts b/src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts index eb401d9cd247118420b04c02a06d3ac1eb975020..f4bf9307cf4b9d0dfbe115e08856a514505d40be 100644 --- a/src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts +++ b/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') diff --git a/src/mol-repr/structure/complex-visual.ts b/src/mol-repr/structure/complex-visual.ts index 0cc0bf9274c6ba4484e871bcc87300770119b993..f42df562b0d99b868cb49759faf5c633082dfd96 100644 --- a/src/mol-repr/structure/complex-visual.ts +++ b/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 diff --git a/src/mol-repr/structure/units-visual.ts b/src/mol-repr/structure/units-visual.ts index bad9e645c3e32c8137c6ef0438b9bdc2219b06ae..f622140015e3bf5e579fb3026f326e9abea19cf0 100644 --- a/src/mol-repr/structure/units-visual.ts +++ b/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 diff --git a/src/mol-repr/structure/visual/polymer-direction-wedge.ts b/src/mol-repr/structure/visual/polymer-direction-wedge.ts index 43712a89d45b50cd00ffd9e810b4d11220c31d45..46523a359151140fdd2acd8b1a2e2b179a3fbea7 100644 --- a/src/mol-repr/structure/visual/polymer-direction-wedge.ts +++ b/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 diff --git a/src/mol-repr/structure/visual/polymer-trace-mesh.ts b/src/mol-repr/structure/visual/polymer-trace-mesh.ts index 8f4f73616d44174479907ca2157e6efadb761475..fd8716ee9399b6f3b9f5b1ff59dea59d7611c48c 100644 --- a/src/mol-repr/structure/visual/polymer-trace-mesh.ts +++ b/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) } \ No newline at end of file diff --git a/src/mol-repr/structure/visual/polymer-tube-mesh.ts b/src/mol-repr/structure/visual/polymer-tube-mesh.ts index 8f433bf01a8c4ba16704f1e13601db57148fd33e..4678499c11b606519af8862339a8fc2892aa3502 100644 --- a/src/mol-repr/structure/visual/polymer-tube-mesh.ts +++ b/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 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 6216c5307bf739bda64629d24387b3754b68e94a..9ecccfa49ec77f9f5587d02ef3c7dc3e7e8bb897 100644 --- a/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts +++ b/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) { diff --git a/src/mol-repr/util.ts b/src/mol-repr/util.ts index d4428fad2a1b15fedbe677377346e0f093156dea..e9e1a5962157dc8d1980230d4bf22a225704d7d4 100644 --- a/src/mol-repr/util.ts +++ b/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) { diff --git a/src/mol-theme/color.ts b/src/mol-theme/color.ts index cab7d59fcd15c3839d33722eed8d6d929908f7ec..a2c2417686935eb95a0ea3385591d38e0f8ba0f5 100644 --- a/src/mol-theme/color.ts +++ b/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> { } diff --git a/src/mol-theme/color/secondary-structure.ts b/src/mol-theme/color/secondary-structure.ts index cb8878f28b77889d830573b5609a3b1c32feed6e..5711250af7834f0bdf688c6817a9d9c323d998d5 100644 --- a/src/mol-theme/color/secondary-structure.ts +++ b/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] diff --git a/src/tests/browser/render-structure.ts b/src/tests/browser/render-structure.ts index 290afa43d6b56c3b607aafcfdf98f8a7ac46724d..7529018f0976d3d9deefa9c5ade68a7867314aa9 100644 --- a/src/tests/browser/render-structure.ts +++ b/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%'