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>();