diff --git a/src/mol-plugin/state/actions/basic.ts b/src/mol-plugin/state/actions/basic.ts index db954502b1e46603f0f8a64fc22ca81aa598c42b..83167be699923c1b3d344a78bdceb81501c5e203 100644 --- a/src/mol-plugin/state/actions/basic.ts +++ b/src/mol-plugin/state/actions/basic.ts @@ -6,7 +6,7 @@ */ import { PluginContext } from 'mol-plugin/context'; -import { StateTree, Transformer, StateObject } from 'mol-state'; +import { StateTree, Transformer, StateObject, State } from 'mol-state'; import { StateAction } from 'mol-state/action'; import { StateSelection } from 'mol-state/state/selection'; import { StateTreeBuilder } from 'mol-state/tree/builder'; @@ -14,9 +14,11 @@ import { ParamDefinition as PD } from 'mol-util/param-definition'; import { PluginStateObject } from '../objects'; import { StateTransforms } from '../transforms'; import { Download } from '../transforms/data'; -import { StructureRepresentation3DHelpers } from '../transforms/representation'; +import { StructureRepresentation3DHelpers, VolumeRepresentation3DHelpers } from '../transforms/representation'; import { getFileInfo, FileInfo } from 'mol-util/file-info'; import { Task } from 'mol-task'; +import { ColorNames } from 'mol-util/color/tables'; +import { VolumeIsoValue } from 'mol-model/volume'; // TODO: "structure/volume parser provider" @@ -171,8 +173,8 @@ export const UpdateTrajectory = StateAction.build({ // export class DataFormatRegistry<D extends PluginStateObject.Data.Binary | PluginStateObject.Data.String, M extends StateObject> { - private _list: { name: string, provider: DataFormatProvider<D, M> }[] = [] - private _map = new Map<string, DataFormatProvider<D, M>>() + private _list: { name: string, provider: DataFormatProvider<D> }[] = [] + private _map = new Map<string, DataFormatProvider<D>>() get default() { return this._list[0]; } get types(): [string, string][] { @@ -185,7 +187,7 @@ export class DataFormatRegistry<D extends PluginStateObject.Data.Binary | Plugin this.add('dscif', DscifProvider) }; - add(name: string, provider: DataFormatProvider<D, M>) { + add(name: string, provider: DataFormatProvider<D>) { this._list.push({ name, provider }) this._map.set(name, provider) } @@ -203,7 +205,7 @@ export class DataFormatRegistry<D extends PluginStateObject.Data.Binary | Plugin throw new Error('no compatible data format provider available') } - get(name: string): DataFormatProvider<D, M> { + get(name: string): DataFormatProvider<D> { if (this._map.has(name)) { return this._map.get(name)! } else { @@ -216,53 +218,82 @@ export class DataFormatRegistry<D extends PluginStateObject.Data.Binary | Plugin } } -interface DataFormatProvider<D extends PluginStateObject.Data.Binary | PluginStateObject.Data.String, M extends StateObject> { +interface DataFormatProvider<D extends PluginStateObject.Data.Binary | PluginStateObject.Data.String> { label: string description: string fileExtensions: string[] isApplicable(info: FileInfo, data: string | Uint8Array): boolean - getDefaultBuilder(b: StateTreeBuilder.To<D>): StateTreeBuilder.To<M> + getDefaultBuilder(ctx: PluginContext, data: StateTreeBuilder.To<D>, state?: State): Task<void> } -const Ccp4Provider: DataFormatProvider<any, any> = { +const Ccp4Provider: DataFormatProvider<any> = { label: 'CCP4/MRC/BRIX', description: 'CCP4/MRC/BRIX', fileExtensions: ['ccp4', 'mrc', 'map'], isApplicable: (info: FileInfo, data: Uint8Array) => { return info.ext === 'ccp4' || info.ext === 'mrc' || info.ext === 'map' }, - getDefaultBuilder: (b: StateTreeBuilder.To<PluginStateObject.Data.Binary>) => { - return b.apply(StateTransforms.Data.ParseCcp4) - .apply(StateTransforms.Model.VolumeFromCcp4) - .apply(StateTransforms.Representation.VolumeRepresentation3D) + getDefaultBuilder: (ctx: PluginContext, data: StateTreeBuilder.To<PluginStateObject.Data.Binary>, state: State) => { + return Task.create('CCP4/MRC/BRIX default builder', async taskCtx => { + const tree = data.apply(StateTransforms.Data.ParseCcp4) + .apply(StateTransforms.Model.VolumeFromCcp4) + .apply(StateTransforms.Representation.VolumeRepresentation3D) + await state.updateTree(tree).runInContext(taskCtx) + }) } } -const Dsn6Provider: DataFormatProvider<any, any> = { +const Dsn6Provider: DataFormatProvider<any> = { label: 'DSN6/BRIX', description: 'DSN6/BRIX', fileExtensions: ['dsn6', 'brix'], isApplicable: (info: FileInfo, data: Uint8Array) => { return info.ext === 'dsn6' || info.ext === 'brix' }, - getDefaultBuilder: (b: StateTreeBuilder.To<PluginStateObject.Data.Binary>) => { - return b.apply(StateTransforms.Data.ParseDsn6) - .apply(StateTransforms.Model.VolumeFromDsn6) - .apply(StateTransforms.Representation.VolumeRepresentation3D) + getDefaultBuilder: (ctx: PluginContext, data: StateTreeBuilder.To<PluginStateObject.Data.Binary>, state: State) => { + return Task.create('DSN6/BRIX default builder', async taskCtx => { + const tree = data.apply(StateTransforms.Data.ParseDsn6) + .apply(StateTransforms.Model.VolumeFromDsn6) + .apply(StateTransforms.Representation.VolumeRepresentation3D) + await state.updateTree(tree).runInContext(taskCtx) + }) } } -const DscifProvider: DataFormatProvider<any, any> = { +const DscifProvider: DataFormatProvider<any> = { label: 'DensityServer CIF', description: 'DensityServer CIF', fileExtensions: ['cif'], isApplicable: (info: FileInfo, data: Uint8Array) => { return info.ext === 'cif' }, - getDefaultBuilder: (b: StateTreeBuilder.To<PluginStateObject.Data.Binary>) => { - return b.apply(StateTransforms.Data.ParseCif, { }) - .apply(StateTransforms.Model.VolumeFromDensityServerCif) - .apply(StateTransforms.Representation.VolumeRepresentation3D) + getDefaultBuilder: (ctx: PluginContext, data: StateTreeBuilder.To<PluginStateObject.Data.Binary>, state: State) => { + return Task.create('DensityServer CIF default builder', async taskCtx => { + const cifBuilder = data.apply(StateTransforms.Data.ParseCif) + const cifStateObject = await state.updateTree(cifBuilder).runInContext(taskCtx) + const b = state.build().to(cifBuilder.ref); + const blocks = cifStateObject.data.blocks.slice(1); // zero block contains query meta-data + let tree: StateTreeBuilder.To<any> + if (blocks.length === 1) { + tree = b + .apply(StateTransforms.Model.VolumeFromDensityServerCif, { blockHeader: blocks[0].header }) + .apply(StateTransforms.Representation.VolumeRepresentation3D, VolumeRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'isosurface', { isoValue: VolumeIsoValue.relative(1.5), alpha: 0.3 })) + } else if (blocks.length === 2) { + tree = b + .apply(StateTransforms.Model.VolumeFromDensityServerCif, { blockHeader: blocks[0].header }) + .apply(StateTransforms.Representation.VolumeRepresentation3D, VolumeRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'isosurface', { isoValue: VolumeIsoValue.relative(1.5), alpha: 0.3 }, 'uniform', { value: ColorNames.blue })) + const vol = tree.to(cifBuilder.ref) + .apply(StateTransforms.Model.VolumeFromDensityServerCif, { blockHeader: blocks[1].header }) + const posParams = VolumeRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'isosurface', { isoValue: VolumeIsoValue.relative(3), alpha: 0.3 }, 'uniform', { value: ColorNames.green }) + tree = vol.apply(StateTransforms.Representation.VolumeRepresentation3D, posParams) + const negParams = VolumeRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'isosurface', { isoValue: VolumeIsoValue.relative(-3), alpha: 0.3 }, 'uniform', { value: ColorNames.red }) + tree = tree.to(vol.ref).apply(StateTransforms.Representation.VolumeRepresentation3D, negParams) + } else { + throw new Error('unknown number of blocks') + } + + await state.updateTree(tree).runInContext(taskCtx); + }) } } @@ -302,9 +333,8 @@ export const OpenVolume = StateAction.build({ const provider = params.format === 'auto' ? ctx.dataFormat.registry.auto(getFileInfo(params.file), dataStateObject) : ctx.dataFormat.registry.get(params.format) const b = state.build().to(data.ref); - const tree = provider.getDefaultBuilder(b).getTree() // need to await the 2nd update the so that the enclosing Task finishes after the update is done. - await state.updateTree(tree).runInContext(taskCtx); + await provider.getDefaultBuilder(ctx, b, state).runInContext(taskCtx) })); export { DownloadDensity }; @@ -341,7 +371,7 @@ const DownloadDensity = StateAction.build({ })(({ params, state }, ctx: PluginContext) => Task.create('Download Density', async taskCtx => { const src = params.source; let downloadParams: Transformer.Params<Download>; - let provider: DataFormatProvider<any, any> + let provider: DataFormatProvider<any> switch (src.name) { case 'url': @@ -386,6 +416,5 @@ const DownloadDensity = StateAction.build({ } const b = state.build().to(data.ref); - const tree = provider.getDefaultBuilder(b).getTree() - await state.updateTree(tree).runInContext(taskCtx); + await provider.getDefaultBuilder(ctx, b, state).runInContext(taskCtx) })); \ No newline at end of file diff --git a/src/mol-plugin/state/transforms/representation.ts b/src/mol-plugin/state/transforms/representation.ts index 31fbb134ff2df51dfc639187c3c87edefb255302..8127153899db4d6dda69ec83c818d38229df2e28 100644 --- a/src/mol-plugin/state/transforms/representation.ts +++ b/src/mol-plugin/state/transforms/representation.ts @@ -19,6 +19,8 @@ import { ExplodeRepresentation3D } from 'mol-plugin/behavior/dynamic/representat import { VolumeData } from 'mol-model/volume'; import { BuiltInVolumeRepresentationsName } from 'mol-repr/volume/registry'; import { VolumeParams } from 'mol-repr/volume/representation'; +import { BuiltInColorThemeName, ColorTheme } from 'mol-theme/color'; +import { BuiltInSizeThemeName, SizeTheme } from 'mol-theme/size'; export namespace StructureRepresentation3DHelpers { export function getDefaultParams(ctx: PluginContext, name: BuiltInStructureRepresentationsName, structure: Structure, structureParams?: Partial<PD.Values<StructureParams>>): Transformer.Params<StructureRepresentation3D> { @@ -162,14 +164,14 @@ export namespace VolumeRepresentation3DHelpers { }) } - export function getDefaultParamsStatic(ctx: PluginContext, name: BuiltInVolumeRepresentationsName, volumeParams?: Partial<PD.Values<VolumeParams>>): Transformer.Params<VolumeRepresentation3D> { + export function getDefaultParamsStatic(ctx: PluginContext, name: BuiltInVolumeRepresentationsName, volumeParams?: Partial<PD.Values<PD.Params>>, colorName?: BuiltInColorThemeName, colorParams?: Partial<ColorTheme.Props>, sizeName?: BuiltInSizeThemeName, sizeParams?: Partial<SizeTheme.Props>): Transformer.Params<VolumeRepresentation3D> { const type = ctx.volumeRepresentation.registry.get(name); - const colorParams = ctx.volumeRepresentation.themeCtx.colorThemeRegistry.get(type.defaultColorTheme).defaultValues; - const sizeParams = ctx.volumeRepresentation.themeCtx.sizeThemeRegistry.get(type.defaultSizeTheme).defaultValues + const colorType = ctx.volumeRepresentation.themeCtx.colorThemeRegistry.get(colorName || type.defaultColorTheme); + const sizeType = ctx.volumeRepresentation.themeCtx.sizeThemeRegistry.get(sizeName || type.defaultSizeTheme); return ({ type: { name, params: volumeParams ? { ...type.defaultValues, ...volumeParams } : type.defaultValues }, - colorTheme: { name: type.defaultColorTheme, params: colorParams }, - sizeTheme: { name: type.defaultSizeTheme, params: sizeParams } + colorTheme: { name: type.defaultColorTheme, params: colorParams ? { ...colorType.defaultValues, ...colorParams } : colorType.defaultValues }, + sizeTheme: { name: type.defaultSizeTheme, params: sizeParams ? { ...sizeType.defaultValues, ...sizeParams } : sizeType.defaultValues } }) } } @@ -234,7 +236,6 @@ const VolumeRepresentation3D = PluginStateTransform.BuiltIn({ repr.setTheme(createTheme(plugin.volumeRepresentation.themeCtx, { volume: a.data }, params)) // TODO set initial state, repr.setState({}) // TODO include isoValue in the label where available - console.log(params.type.params); await repr.createOrUpdate(props, a.data).runInContext(ctx); return new SO.Volume.Representation3D(repr, { label: provider.label }); });