diff --git a/src/mol-model-props/common/custom-element-property.ts b/src/mol-model-props/common/custom-element-property.ts index f5e2a6ff7e86a9a6a1845acb5d186cc851fe4529..87f84d5a50c500b938f505544720d57a260d9623 100644 --- a/src/mol-model-props/common/custom-element-property.ts +++ b/src/mol-model-props/common/custom-element-property.ts @@ -67,7 +67,7 @@ namespace CustomElementProperty { function getStatic(e: StructureElement) { return e.unit.model._staticPropertyData[name].get(e.element); } function getDynamic(e: StructureElement) { return e.unit.model._staticPropertyData[name].get(e.element); } - const propertyProvider: CustomPropertyRegistry.Provider = { + const propertyProvider: CustomPropertyRegistry.ModelProvider = { option: [name, params.display], descriptor: Descriptor, defaultSelected: !!params.autoAttach, diff --git a/src/mol-model-props/common/custom-property-registry.ts b/src/mol-model-props/common/custom-property-registry.ts index 977cade54990a71dd9b3f821cc89bd02a91c78c6..066d66aacd09c06e772cc3ac44b3a598e88c9c21 100644 --- a/src/mol-model-props/common/custom-property-registry.ts +++ b/src/mol-model-props/common/custom-property-registry.ts @@ -1,39 +1,40 @@ /** - * 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 David Sehnal <david.sehnal@gmail.com> + * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { CustomPropertyDescriptor, Model } from 'mol-model/structure'; +import { CustomPropertyDescriptor, Model, Structure } from 'mol-model/structure'; import { OrderedMap } from 'immutable'; import { ParamDefinition } from 'mol-util/param-definition'; import { Task } from 'mol-task'; export { CustomPropertyRegistry } -class CustomPropertyRegistry { - private providers = OrderedMap<string, CustomPropertyRegistry.Provider>().asMutable(); +class CustomPropertyRegistry<T = never> { + private providers = OrderedMap<string, CustomPropertyRegistry.Provider<T>>().asMutable(); - getSelect(model: Model) { + getSelect(object: T) { const values = this.providers.values(); const options: [string, string][] = [], selected: string[] = []; while (true) { const v = values.next(); if (v.done) break; - if (!v.value.attachableTo(model)) continue; + if (!v.value.attachableTo(object)) continue; options.push(v.value.option); if (v.value.defaultSelected) selected.push(v.value.option[0]); } return ParamDefinition.MultiSelect(selected, options); } - getDefault(model: Model) { + getDefault(object: T) { const values = this.providers.values(); const selected: string[] = []; while (true) { const v = values.next(); if (v.done) break; - if (!v.value.attachableTo(model)) continue; + if (!v.value.attachableTo(object)) continue; if (v.value.defaultSelected) selected.push(v.value.option[0]); } return selected; @@ -45,7 +46,7 @@ class CustomPropertyRegistry { return this.providers.get(name); } - register(provider: CustomPropertyRegistry.Provider) { + register(provider: CustomPropertyRegistry.Provider<T>) { this.providers.set(provider.descriptor.name, provider); } @@ -55,11 +56,14 @@ class CustomPropertyRegistry { } namespace CustomPropertyRegistry { - export interface Provider { + export interface Provider<T> { option: [string, string], defaultSelected: boolean, descriptor: CustomPropertyDescriptor<any, any>, - attachableTo: (model: Model) => boolean, - attach: (model: Model) => Task<boolean> + attachableTo: (object: T) => boolean, + attach: (object: T) => Task<boolean> } + + export type ModelProvider = Provider<Model> + export type StructureProvider = Provider<Structure> } \ No newline at end of file diff --git a/src/mol-model-props/computed/secondary-structure.ts b/src/mol-model-props/computed/secondary-structure.ts new file mode 100644 index 0000000000000000000000000000000000000000..82cb45c68cf5b62276e691445e90003f58385490 --- /dev/null +++ b/src/mol-model-props/computed/secondary-structure.ts @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { CustomPropertyDescriptor, Structure } from 'mol-model/structure'; +import { Task } from 'mol-task'; + +export namespace ComputedSecondaryStructure { + export type Property = {} // TODO + + export function get(structure: Structure): Property | undefined { + return structure.currentPropertyData.__ComputedSecondaryStructure__; + } + function set(structure: Structure, prop: Property) { + (structure.currentPropertyData.__ComputedSecondaryStructure__ as Property) = prop; + } + + export function createAttachTask() { + return (structure: Structure) => Task.create('Compute Secondary Structure', async ctx => { + if (get(structure)) return true; + return await attachFromCifOrCompute(structure, ctx) + }); + } + + export const Descriptor = CustomPropertyDescriptor({ + isStatic: true, + name: 'molstar_computed_secondary_structure', + // TODO `cifExport` and `symbol` + }); + + export async function attachFromCifOrCompute(structure: Structure, params: { + // TODO params + }) { + if (structure.customPropertyDescriptors.has(Descriptor)) return true; + + const compSecStruc = computeSecondaryStructure(structure) + + structure.customPropertyDescriptors.add(Descriptor); + set(structure, compSecStruc); + return true; + } +} + +// 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 {} +} \ No newline at end of file diff --git a/src/mol-plugin/behavior/dynamic/custom-props.ts b/src/mol-plugin/behavior/dynamic/custom-props.ts index caba1bc138bf5927c0b6572a6ef1bf3c95568560..6612db12a851d88f67b8ee30148a0a5919b4657e 100644 --- a/src/mol-plugin/behavior/dynamic/custom-props.ts +++ b/src/mol-plugin/behavior/dynamic/custom-props.ts @@ -1,9 +1,10 @@ /** - * 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 David Sehnal <david.sehnal@gmail.com> * @author Alexander Rose <alexander.rose@weirdbyte.de> */ +export { MolstarSecondaryStructure } from './custom-props/computed/secondary-structure' export { PDBeStructureQualityReport } from './custom-props/pdbe/structure-quality-report' export { RCSBAssemblySymmetry } from './custom-props/rcsb/assembly-symmetry' \ No newline at end of file diff --git a/src/mol-plugin/behavior/dynamic/custom-props/computed/secondary-structure.ts b/src/mol-plugin/behavior/dynamic/custom-props/computed/secondary-structure.ts new file mode 100644 index 0000000000000000000000000000000000000000..72574abce296631ff7734b36d4b6049fa6a7e547 --- /dev/null +++ b/src/mol-plugin/behavior/dynamic/custom-props/computed/secondary-structure.ts @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { ParamDefinition as PD } from 'mol-util/param-definition'; +import { PluginBehavior } from '../../../behavior'; +import { CustomPropertyRegistry } from 'mol-model-props/common/custom-property-registry'; +import { ComputedSecondaryStructure } from 'mol-model-props/computed/secondary-structure'; + +export const MolstarSecondaryStructure = PluginBehavior.create<{ autoAttach: boolean }>({ + name: 'molstar-computed-secondary-structure-prop', + category: 'custom-props', + display: { name: 'Computed Secondary Structure' }, + ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean }> { + private attach = ComputedSecondaryStructure.createAttachTask(); + + private provider: CustomPropertyRegistry.StructureProvider = { + option: [ComputedSecondaryStructure.Descriptor.name, 'Computed Secondary Structure'], + descriptor: ComputedSecondaryStructure.Descriptor, + defaultSelected: this.params.autoAttach, + attachableTo: () => true, + attach: this.attach + } + + register(): void { + this.ctx.customStructureProperties.register(this.provider); + } + + update(p: { autoAttach: boolean }) { + let updated = this.params.autoAttach !== p.autoAttach + this.params.autoAttach = p.autoAttach; + this.provider.defaultSelected = p.autoAttach; + return updated; + } + + unregister() { + this.ctx.customStructureProperties.unregister(ComputedSecondaryStructure.Descriptor.name); + } + }, + params: () => ({ + autoAttach: PD.Boolean(false) + }) +}); \ No newline at end of file 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 5b8bfdc183f3632ac6dccdc41119bb11b86d7663..eb401d9cd247118420b04c02a06d3ac1eb975020 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 @@ -24,7 +24,7 @@ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: bo this.ctx.fetch ); - private provider: CustomPropertyRegistry.Provider = { + private provider: CustomPropertyRegistry.ModelProvider = { option: [StructureQualityReport.Descriptor.name, 'PDBe Structure Quality Report'], descriptor: StructureQualityReport.Descriptor, defaultSelected: this.params.autoAttach, diff --git a/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts b/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts index 0d65bbc92b7823b7d2eea29ebc2274198f06e665..a9d314aefd6f830e1c799c8ce0664d7221cc1c58 100644 --- a/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts +++ b/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts @@ -21,7 +21,7 @@ export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean }> { private attach = AssemblySymmetry.createAttachTask(this.ctx.fetch); - private provider: CustomPropertyRegistry.Provider = { + private provider: CustomPropertyRegistry.ModelProvider = { option: [AssemblySymmetry.Descriptor.name, 'RCSB Assembly Symmetry'], descriptor: AssemblySymmetry.Descriptor, defaultSelected: this.params.autoAttach, diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts index 85da6b7514b8073897fb16a3c4f7cdf1eb5237ea..d35533a86d220de1f817fdd6b665b5c98063a986 100644 --- a/src/mol-plugin/context.ts +++ b/src/mol-plugin/context.ts @@ -35,6 +35,7 @@ import { SubstructureParentHelper } from './util/substructure-parent-helper'; import { Representation } from 'mol-repr/representation'; import { ModifiersKeys } from 'mol-util/input/input-observer'; import { isProductionMode, isDebugMode } from 'mol-util/debug'; +import { Model, Structure } from 'mol-model/structure'; export class PluginContext { private disposed = false; @@ -99,7 +100,8 @@ export class PluginContext { registry: new DataFormatRegistry() } - readonly customModelProperties = new CustomPropertyRegistry(); + readonly customModelProperties = new CustomPropertyRegistry<Model>(); + readonly customStructureProperties = new CustomPropertyRegistry<Structure>(); readonly customParamEditors = new Map<string, StateTransformParameters.Class>(); readonly helpers = { diff --git a/src/mol-plugin/index.ts b/src/mol-plugin/index.ts index 8b9610bba7b1695ba377016a29bc5a396fc25eec..8f04b4277a76976fd9c0645364b0c19834b42708 100644 --- a/src/mol-plugin/index.ts +++ b/src/mol-plugin/index.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 David Sehnal <david.sehnal@gmail.com> * @author Alexander Rose <alexander.rose@weirdbyte.de> @@ -25,6 +25,7 @@ export const DefaultPluginSpec: PluginSpec = { PluginSpec.Action(StateActions.DataFormat.OpenFile), PluginSpec.Action(StateActions.Structure.CreateComplexRepresentation), PluginSpec.Action(StateActions.Structure.EnableModelCustomProps), + PluginSpec.Action(StateActions.Structure.EnableStructureCustomProps), // Volume streaming PluginSpec.Action(InitVolumeStreaming), @@ -35,6 +36,7 @@ export const DefaultPluginSpec: PluginSpec = { PluginSpec.Action(StateTransforms.Data.ParseCif), PluginSpec.Action(StateTransforms.Data.ParseCcp4), PluginSpec.Action(StateTransforms.Data.ParseDsn6), + PluginSpec.Action(StateTransforms.Model.TrajectoryFromMmCif), PluginSpec.Action(StateTransforms.Model.TrajectoryFromPDB), PluginSpec.Action(StateTransforms.Model.StructureAssemblyFromModel), @@ -43,13 +45,14 @@ export const DefaultPluginSpec: PluginSpec = { PluginSpec.Action(StateTransforms.Model.StructureFromModel), PluginSpec.Action(StateTransforms.Model.ModelFromTrajectory), PluginSpec.Action(StateTransforms.Model.UserStructureSelection), - PluginSpec.Action(StateTransforms.Volume.VolumeFromCcp4), PluginSpec.Action(StateTransforms.Representation.StructureRepresentation3D), PluginSpec.Action(StateTransforms.Representation.StructureLabels3D), PluginSpec.Action(StateTransforms.Representation.ExplodeStructureRepresentation3D), PluginSpec.Action(StateTransforms.Representation.UnwindStructureAssemblyRepresentation3D), PluginSpec.Action(StateTransforms.Representation.OverpaintStructureRepresentation3D), PluginSpec.Action(StateTransforms.Representation.TransparencyStructureRepresentation3D), + + PluginSpec.Action(StateTransforms.Volume.VolumeFromCcp4), PluginSpec.Action(StateTransforms.Representation.VolumeRepresentation3D), PluginSpec.Action(StateActions.Structure.StructureFromSelection), @@ -60,6 +63,7 @@ export const DefaultPluginSpec: PluginSpec = { PluginSpec.Behavior(PluginBehaviors.Representation.DefaultLociLabelProvider), PluginSpec.Behavior(PluginBehaviors.Camera.FocusLociOnSelect, { minRadius: 8, extraRadius: 4 }), // PluginSpec.Behavior(PluginBehaviors.Labels.SceneLabels), + PluginSpec.Behavior(PluginBehaviors.CustomProps.MolstarSecondaryStructure, { autoAttach: true }), PluginSpec.Behavior(PluginBehaviors.CustomProps.PDBeStructureQualityReport, { autoAttach: true }), PluginSpec.Behavior(PluginBehaviors.CustomProps.RCSBAssemblySymmetry, { autoAttach: true }), PluginSpec.Behavior(StructureRepresentationInteraction) diff --git a/src/mol-plugin/state/actions/structure.ts b/src/mol-plugin/state/actions/structure.ts index c32e25c47e360c1e12bb71c36e849c9b50458df4..e8f8bfb09532de95672039efcd5215046e474232 100644 --- a/src/mol-plugin/state/actions/structure.ts +++ b/src/mol-plugin/state/actions/structure.ts @@ -12,7 +12,7 @@ import { PluginStateObject } from '../objects'; import { StateTransforms } from '../transforms'; import { Download } from '../transforms/data'; import { StructureRepresentation3DHelpers } from '../transforms/representation'; -import { CustomModelProperties, StructureSelection } from '../transforms/model'; +import { CustomModelProperties, StructureSelection, CustomStructureProperties } from '../transforms/model'; import { DataFormatProvider, guessCifVariant } from './data-format'; import { FileInfo } from 'mol-util/file-info'; import { Task } from 'mol-task'; @@ -273,10 +273,10 @@ export const UpdateTrajectory = StateAction.build({ }); export const EnableModelCustomProps = StateAction.build({ - display: { name: 'Custom Properties', description: 'Enable the addition of custom properties to the model.' }, + display: { name: 'Custom Model Properties', description: 'Enable the addition of custom properties to the model.' }, from: PluginStateObject.Molecule.Model, params(a, ctx: PluginContext) { - if (!a) return { properties: PD.MultiSelect([], [], { description: 'A list of property descriptor ids.' }) }; + if (!a) return { properties: PD.MultiSelect([], [], { description: 'A list of model property descriptor ids.' }) }; return { properties: ctx.customModelProperties.getSelect(a.data) }; }, isApplicable(a, t, ctx: PluginContext) { @@ -287,6 +287,21 @@ export const EnableModelCustomProps = StateAction.build({ return state.updateTree(root); }); +export const EnableStructureCustomProps = StateAction.build({ + display: { name: 'Custom Structure Properties', description: 'Enable the addition of custom properties to the structure.' }, + from: PluginStateObject.Molecule.Structure, + params(a, ctx: PluginContext) { + if (!a) return { properties: PD.MultiSelect([], [], { description: 'A list of structure property descriptor ids.' }) }; + return { properties: ctx.customStructureProperties.getSelect(a.data) }; + }, + isApplicable(a, t, ctx: PluginContext) { + return t.transformer !== CustomStructureProperties; + } +})(({ ref, params, state }, ctx: PluginContext) => { + const root = state.build().to(ref).insert(CustomStructureProperties, params); + return state.updateTree(root); +}); + export const TransformStructureConformation = StateAction.build({ display: { name: 'Transform Conformation' }, from: PluginStateObject.Molecule.Structure, diff --git a/src/mol-plugin/state/transforms/model.ts b/src/mol-plugin/state/transforms/model.ts index 01ef8d7ddeb870f743cf4a733fe4efccc1ab3a63..59cae2d75540385ce6740bb0bdf2c72ddfbe6189 100644 --- a/src/mol-plugin/state/transforms/model.ts +++ b/src/mol-plugin/state/transforms/model.ts @@ -40,6 +40,7 @@ export { StructureSelection }; export { UserStructureSelection }; export { StructureComplexElement }; export { CustomModelProperties }; +export { CustomStructureProperties }; type TrajectoryFromBlob = typeof TrajectoryFromBlob const TrajectoryFromBlob = PluginStateTransform.BuiltIn({ @@ -189,7 +190,8 @@ const StructureAssemblyFromModel = PluginStateTransform.BuiltIn({ const model = a.data; const ids = model.symmetry.assemblies.map(a => [a.id, `${a.id}: ${stringToWords(a.details)}`] as [string, string]); ids.push(['deposited', 'Deposited']); - return { id: PD.Optional(PD.Select(ids[0][0], ids, { label: 'Asm Id', description: 'Assembly Id' })) }; + return { + id: PD.Optional(PD.Select(ids[0][0], ids, { label: 'Asm Id', description: 'Assembly Id' })) }; } })({ apply({ a, params }, plugin: PluginContext) { @@ -432,18 +434,43 @@ const CustomModelProperties = PluginStateTransform.BuiltIn({ })({ apply({ a, params }, ctx: PluginContext) { return Task.create('Custom Props', async taskCtx => { - await attachProps(a.data, ctx, taskCtx, params.properties); - return new SO.Molecule.Model(a.data, { label: 'Props', description: `${params.properties.length} Selected` }); + await attachModelProps(a.data, ctx, taskCtx, params.properties); + return new SO.Molecule.Model(a.data, { label: 'Model Props', description: `${params.properties.length} Selected` }); }); } }); -async function attachProps(model: Model, ctx: PluginContext, taskCtx: RuntimeContext, names: string[]) { +async function attachModelProps(model: Model, ctx: PluginContext, taskCtx: RuntimeContext, names: string[]) { for (const name of names) { const p = ctx.customModelProperties.get(name); await p.attach(model).runInContext(taskCtx); } } +type CustomStructureProperties = typeof CustomStructureProperties +const CustomStructureProperties = PluginStateTransform.BuiltIn({ + name: 'custom-structure-properties', + display: { name: 'Custom Structure Properties' }, + from: SO.Molecule.Structure, + to: SO.Molecule.Structure, + params: (a, ctx: PluginContext) => { + if (!a) return { properties: PD.MultiSelect([], [], { description: 'A list of property descriptor ids.' }) }; + return { properties: ctx.customStructureProperties.getSelect(a.data) }; + } +})({ + apply({ a, params }, ctx: PluginContext) { + return Task.create('Custom Props', async taskCtx => { + await attachStructureProps(a.data, ctx, taskCtx, params.properties); + return new SO.Molecule.Structure(a.data, { label: 'Structure Props', description: `${params.properties.length} Selected` }); + }); + } +}); +async function attachStructureProps(structure: Structure, ctx: PluginContext, taskCtx: RuntimeContext, names: string[]) { + for (const name of names) { + const p = ctx.customStructureProperties.get(name); + await p.attach(structure).runInContext(taskCtx); + } +} + export { ShapeFromPly } type ShapeFromPly = typeof ShapeFromPly const ShapeFromPly = PluginStateTransform.BuiltIn({