diff --git a/src/mol-plugin-state/actions/structure.ts b/src/mol-plugin-state/actions/structure.ts index e53e1fb0c620bef3888a8634964c410d89be139b..1d4e898d06239ca200a4ac4676c1ee1c2da30d7f 100644 --- a/src/mol-plugin-state/actions/structure.ts +++ b/src/mol-plugin-state/actions/structure.ts @@ -235,16 +235,16 @@ const DownloadStructure = StateAction.build({ const createRepr = !params.source.params.structure.noRepresentation; if (downloadParams.length > 0 && asTrajectory) { - const traj = createSingleTrajectoryModel(downloadParams, state.build()); - const struct = createStructure(traj, supportProps, src.params.structure.type); + const traj = await createSingleTrajectoryModel(plugin, state, downloadParams); + const struct = createStructure(state.build().to(traj), supportProps, src.params.structure.type); await state.updateTree(struct, { revertIfAborted: true }).runInContext(ctx); if (createRepr) { await plugin.structureRepresentation.manager.apply(struct.ref, plugin.structureRepresentation.manager.defaultProvider); } } else { for (const download of downloadParams) { - const data = state.build().toRoot().apply(StateTransforms.Data.Download, download, { state: { isGhost: true } }); - const traj = createModelTree(data, format); + const data = await plugin.builders.data.download(download, { state: { isGhost: true } }); + const traj = createModelTree(state.build().to(data), format); const struct = createStructure(traj, supportProps, src.params.structure.type); await state.updateTree(struct, { revertIfAborted: true }).runInContext(ctx); @@ -264,16 +264,21 @@ function getDownloadParams(src: string, url: (id: string) => string, label: (id: return ret; } -function createSingleTrajectoryModel(sources: StateTransformer.Params<Download>[], b: StateBuilder.Root) { - return b.toRoot() - .apply(StateTransforms.Data.DownloadBlob, { - sources: sources.map((src, i) => ({ id: '' + i, url: src.url, isBinary: src.isBinary })), - maxConcurrency: 6 - }, { state: { isGhost: true } }).apply(StateTransforms.Data.ParseBlob, { +async function createSingleTrajectoryModel(plugin: PluginContext, state: State, sources: StateTransformer.Params<Download>[]) { + const data = await plugin.builders.data.downloadBlob({ + sources: sources.map((src, i) => ({ id: '' + i, url: src.url, isBinary: src.isBinary })), + maxConcurrency: 6 + }, { state: { isGhost: true } }); + + const trajectory = state.build().to(data) + .apply(StateTransforms.Data.ParseBlob, { formats: sources.map((_, i) => ({ id: '' + i, format: 'cif' as 'cif' })) }, { state: { isGhost: true } }) .apply(StateTransforms.Model.TrajectoryFromBlob) .apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 }); + + await plugin.runTask(state.updateTree(trajectory, { revertIfAborted: true })); + return trajectory.selector; } export function createModelTree(b: StateBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, format: StructureFormat = 'cif') { diff --git a/src/mol-plugin-state/builder/data.ts b/src/mol-plugin-state/builder/data.ts new file mode 100644 index 0000000000000000000000000000000000000000..fbd224c12a5246284075e577c1e14055cd60ae1f --- /dev/null +++ b/src/mol-plugin-state/builder/data.ts @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { StateTransformer, StateTransform } from '../../mol-state'; +import { PluginContext } from '../../mol-plugin/context'; +import { Download, ReadFile, DownloadBlob, RawData } from '../transforms/data'; +import { getFileInfo } from '../../mol-util/file-info'; + +export class DataBuilder { + private get dataState() { + return this.plugin.state.dataState; + } + + async rawData(params: StateTransformer.Params<RawData>, options?: Partial<StateTransform.Options>) { + const data = this.dataState.build().toRoot().apply(RawData, params, options); + await this.plugin.runTask(this.dataState.updateTree(data)); + return data.selector; + } + + async download(params: StateTransformer.Params<Download>, options?: Partial<StateTransform.Options>) { + const data = this.dataState.build().toRoot().apply(Download, params, options); + await this.plugin.runTask(this.dataState.updateTree(data)); + return data.selector; + } + + async downloadBlob(params: StateTransformer.Params<DownloadBlob>, options?: Partial<StateTransform.Options>) { + const data = this.dataState.build().toRoot().apply(DownloadBlob, params, options); + await this.plugin.runTask(this.dataState.updateTree(data)); + return data.selector; + } + + async readFile(params: StateTransformer.Params<ReadFile>, options?: Partial<StateTransform.Options>) { + const data = this.dataState.build().toRoot().apply(ReadFile, params, options); + const fileInfo = getFileInfo(params.file); + await this.plugin.runTask(this.dataState.updateTree(data)); + return { data: data.selector, fileInfo }; + } + + constructor(public plugin: PluginContext) { + } +} \ No newline at end of file diff --git a/src/mol-plugin-state/transforms/data.ts b/src/mol-plugin-state/transforms/data.ts index 8c2866c7b715e525deeb5870520698eebb3fd3eb..2f0c474f19170de204c42c5556524b91d4e55fd3 100644 --- a/src/mol-plugin-state/transforms/data.ts +++ b/src/mol-plugin-state/transforms/data.ts @@ -17,6 +17,7 @@ import * as CCP4 from '../../mol-io/reader/ccp4/parser' import * as DSN6 from '../../mol-io/reader/dsn6/parser' import * as PLY from '../../mol-io/reader/ply/parser' import { parsePsf } from '../../mol-io/reader/psf/parser'; +import { isTypedArray } from '../../mol-data/db/column-helpers'; export { Download } type Download = typeof Download @@ -95,6 +96,38 @@ const DownloadBlob = PluginStateTransform.BuiltIn({ // } }); +export { RawData } +type RawData = typeof RawData +const RawData = PluginStateTransform.BuiltIn({ + name: 'raw-data', + display: { name: 'Raw Data', description: 'Raw data supplied by value.' }, + from: [SO.Root], + to: [SO.Data.String, SO.Data.Binary], + params: { + data: PD.Value<string | number[]>('', { isHidden: true }), + label: PD.Optional(PD.Text('')) + } +})({ + apply({ params: p }) { + return Task.create('Raw Data', async () => { + if (typeof p.data !== 'string' && isTypedArray(p.data)) { + throw new Error('Supplied binary data must be a plain array.'); + } + return typeof p.data === 'string' + ? new SO.Data.String(p.data as string, { label: p.label ? p.label : 'String' }) + : new SO.Data.Binary(new Uint8Array(p.data), { label: p.label ? p.label : 'Binary' }); + }); + }, + update({ oldParams, newParams, b }) { + if (oldParams.data !== newParams.data) return StateTransformer.UpdateResult.Recreate; + if (oldParams.label !== newParams.label) { + b.label = newParams.label || b.label; + return StateTransformer.UpdateResult.Updated; + } + return StateTransformer.UpdateResult.Unchanged; + } +}); + export { ReadFile } type ReadFile = typeof ReadFile const ReadFile = PluginStateTransform.BuiltIn({ diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts index 7a4c479b3c202e04ce43d67f6aee45d2625f09dc..c57da37a5c34b29a46711e78130030e24f9a2590 100644 --- a/src/mol-plugin/context.ts +++ b/src/mol-plugin/context.ts @@ -46,6 +46,7 @@ import { ViewportScreenshotHelper } from './util/viewport-screenshot'; import { StructureRepresentationManager } from '../mol-plugin-state/representation/structure'; import { CustomProperty } from '../mol-model-props/common/custom-property'; import { PluginConfigManager } from './config'; +import { DataBuilder } from '../mol-plugin-state/builder/data'; export class PluginContext { private disposed = false; @@ -124,6 +125,10 @@ export class PluginContext { registry: new DataFormatRegistry() } as const + readonly builders = { + data: new DataBuilder(this) + }; + readonly customModelProperties = new CustomProperty.Registry<Model>(); readonly customStructureProperties = new CustomProperty.Registry<Structure>(); readonly customParamEditors = new Map<string, StateTransformParameters.Class>();