diff --git a/src/mol-plugin-state/actions/structure.ts b/src/mol-plugin-state/actions/structure.ts index 491bb1d843ee12590bda83f8e6911bedfc83c571..715bc094bcd3beb881847c5bba9040caaeae1b6e 100644 --- a/src/mol-plugin-state/actions/structure.ts +++ b/src/mol-plugin-state/actions/structure.ts @@ -227,24 +227,28 @@ const DownloadStructure = StateAction.build({ await state.transaction(async () => { if (downloadParams.length > 0 && asTrajectory) { - const data = await plugin.builders.data.downloadBlob({ + const blob = await plugin.builders.data.downloadBlob({ sources: downloadParams.map((src, i) => ({ id: '' + i, url: src.url, isBinary: src.isBinary })), maxConcurrency: 6 }, { state: { isGhost: true } }); - const traj = await plugin.builders.structure.parseTrajectory(data, { - formats: downloadParams.map((_, i) => ({ id: '' + i, format: 'cif' as 'cif' })) + const { structure } = await plugin.builders.structure.parseStructure({ + blob, + blobParams: { formats: downloadParams.map((_, i) => ({ id: '' + i, format: 'cif' as 'cif' })) }, + modelProperties: supportProps, + structureProperties: supportProps }); - const { model } = await plugin.builders.structure.createModel(traj, { properties: supportProps }); - const { structure } = await plugin.builders.structure.createStructure(model, { structure: src.params.structure.type, properties: supportProps }); if (createRepr) { await plugin.builders.representation.structurePreset(structure, 'auto'); } } else { for (const download of downloadParams) { const data = await plugin.builders.data.download(download, { state: { isGhost: true } }); - const traj = await plugin.builders.structure.parseTrajectory(data, format); - const { model } = await plugin.builders.structure.createModel(traj, { properties: supportProps }); - const { structure } = await plugin.builders.structure.createStructure(model, { structure: src.params.structure.type, properties: supportProps }); + const { structure } = await plugin.builders.structure.parseStructure({ + data, + dataFormat: format, + modelProperties: supportProps, + structureProperties: supportProps + }); if (createRepr) { await plugin.builders.representation.structurePreset(structure, 'auto'); } @@ -366,10 +370,7 @@ export const EnableModelCustomProps = StateAction.build({ isApplicable(a, t, ctx: PluginContext) { return t.transformer !== CustomModelProperties; } -})(({ ref, params, state }, ctx: PluginContext) => { - const root = state.build().to(ref).insert(CustomModelProperties, params); - return state.updateTree(root); -}); +})(({ ref, params }, ctx: PluginContext) => ctx.builders.structure.insertModelProperties(ref, params)); export const EnableStructureCustomProps = StateAction.build({ display: { name: 'Custom Structure Properties', description: 'Enable parameters for custom properties of the structure.' }, @@ -380,10 +381,7 @@ export const EnableStructureCustomProps = StateAction.build({ 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); -}); +})(({ ref, params }, ctx: PluginContext) => ctx.builders.structure.insertStructureProperties(ref, params)); export const TransformStructureConformation = StateAction.build({ display: { name: 'Transform Conformation' }, diff --git a/src/mol-plugin-state/builder/structure.ts b/src/mol-plugin-state/builder/structure.ts index 9eb66bdbb4a47a34369b05443203c74708d23879..047c3178599ba0bd691910cee33c20f1e425f361 100644 --- a/src/mol-plugin-state/builder/structure.ts +++ b/src/mol-plugin-state/builder/structure.ts @@ -64,6 +64,39 @@ export class StructureBuilder { return trajectory.selector; } + async parseStructure(params: { + data?: StateObjectRef<SO.Data.Binary | SO.Data.String>, + dataFormat?: TrajectoryFormat, + blob?: StateObjectRef<SO.Data.Blob> + blobParams?: StateTransformer.Params<StateTransforms['Data']['ParseBlob']>, + model?: StateTransformer.Params<StateTransforms['Model']['ModelFromTrajectory']>, + modelProperties?: boolean | StateTransformer.Params<StateTransforms['Model']['CustomModelProperties']>, + structure?: RootStructureDefinition.Params, + structureProperties?: boolean | StateTransformer.Params<StateTransforms['Model']['CustomStructureProperties']> + }) { + const trajectory = params.data + ? await this.parseTrajectory(params.data, params.dataFormat! || 'cif') + : await this.parseTrajectoryBlob(params.blob!, params.blobParams!); + + const model = await this.createModel(trajectory, params.model); + const modelProperties = !!params.modelProperties + ? await this.insertModelProperties(model, typeof params?.modelProperties !== 'boolean' ? params?.modelProperties : void 0) : void 0; + + const structure = await this.createStructure(modelProperties || model, params.structure); + const structureProperties = !!params.structureProperties + ? await this.insertStructureProperties(structure, typeof params?.structureProperties !== 'boolean' ? params?.structureProperties : void 0) : void 0; + + return { + trajectory, + model: modelProperties || model, + modelBase: model, + modelProperties, + structure: structureProperties || structure, + structureBase: structure, + structureProperties + }; + } + async parseTrajectory(data: StateObjectRef<SO.Data.Binary | SO.Data.String>, format: TrajectoryFormat): Promise<StateObjectSelector<SO.Molecule.Trajectory>> async parseTrajectory(blob: StateObjectRef<SO.Data.Blob>, params: StateTransformer.Params<StateTransforms['Data']['ParseBlob']>): Promise<StateObjectSelector<SO.Molecule.Trajectory>> async parseTrajectory(data: StateObjectRef, params: any) { @@ -79,43 +112,57 @@ export class StructureBuilder { } } - async createModel(trajectory: StateObjectRef<SO.Molecule.Trajectory>, params?: { - model?: StateTransformer.Params<StateTransforms['Model']['ModelFromTrajectory']>, - properties?: boolean | StateTransformer.Params<StateTransforms['Model']['CustomModelProperties']> - }) { + async createModel(trajectory: StateObjectRef<SO.Molecule.Trajectory>, params?: StateTransformer.Params<StateTransforms['Model']['ModelFromTrajectory']>) { const state = this.dataState; const model = state.build().to(trajectory) - .apply(StateTransforms.Model.ModelFromTrajectory, params?.model || void 0, { tags: StructureBuilderTags.Model }); + .apply(StateTransforms.Model.ModelFromTrajectory, params || { modelIndex: 0 }, { tags: StructureBuilderTags.Model }); - const props = !!params?.properties - ? model.apply(StateTransforms.Model.CustomModelProperties, typeof params?.properties !== 'boolean' ? params?.properties : void 0, { tags: StructureBuilderTags.ModelProperties, isDecorator: true }) - : void 0; + // const props = !!params?.properties + // ? model.apply(StateTransforms.Model.CustomModelProperties, typeof params?.properties !== 'boolean' ? params?.properties : void 0, { tags: StructureBuilderTags.ModelProperties, isDecorator: true }) + // : void 0; await this.plugin.runTask(this.dataState.updateTree(model, { revertOnError: true })); - const modelSelector = model.selector, propertiesSelector = props?.selector; + return model.selector; - return { model: propertiesSelector || modelSelector, index: modelSelector, properties: propertiesSelector }; + // const modelSelector = model.selector, propertiesSelector = props?.selector; + + // return { model: propertiesSelector || modelSelector, index: modelSelector, properties: propertiesSelector }; } - async createStructure(model: StateObjectRef<SO.Molecule.Model>, params?: { - structure?: RootStructureDefinition.Params, - properties?: boolean | StateTransformer.Params<StateTransforms['Model']['CustomStructureProperties']> - }) { + async insertModelProperties(model: StateObjectRef<SO.Molecule.Model>, params?: StateTransformer.Params<StateTransforms['Model']['CustomModelProperties']>) { + const state = this.dataState; + const props = state.build().to(model) + .apply(StateTransforms.Model.CustomModelProperties, params, { tags: StructureBuilderTags.ModelProperties, isDecorator: true }); + await this.plugin.runTask(this.dataState.updateTree(props, { revertOnError: true })); + return props.selector; + } + + async createStructure(model: StateObjectRef<SO.Molecule.Model>, params?: RootStructureDefinition.Params) { const state = this.dataState; const structure = state.build().to(model) - .apply(StateTransforms.Model.StructureFromModel, { type: params?.structure || { name: 'assembly', params: { } } }, { tags: StructureBuilderTags.Structure }); + .apply(StateTransforms.Model.StructureFromModel, { type: params || { name: 'assembly', params: { } } }, { tags: StructureBuilderTags.Structure }); - const props = !!params?.properties - ? structure.apply(StateTransforms.Model.CustomStructureProperties, typeof params?.properties !== 'boolean' ? params?.properties : void 0, { tags: StructureBuilderTags.StructureProperties, isDecorator: true }) - : void 0; + // const props = !!params?.properties + // ? structure.apply(StateTransforms.Model.CustomStructureProperties, typeof params?.properties !== 'boolean' ? params?.properties : void 0, { tags: StructureBuilderTags.StructureProperties, isDecorator: true }) + // : void 0; await this.plugin.runTask(this.dataState.updateTree(structure, { revertOnError: true })); - const structureSelector = structure.selector, propertiesSelector = props?.selector; + return structure.selector; - return { structure: propertiesSelector || structureSelector, definition: structureSelector, properties: propertiesSelector }; + // const structureSelector = structure.selector, propertiesSelector = props?.selector; + + // return { structure: propertiesSelector || structureSelector, definition: structureSelector, properties: propertiesSelector }; + } + + async insertStructureProperties(structure: StateObjectRef<SO.Molecule.Structure>, params?: StateTransformer.Params<StateTransforms['Model']['CustomStructureProperties']>) { + const state = this.dataState; + const props = state.build().to(structure) + .apply(StateTransforms.Model.CustomStructureProperties, params, { tags: StructureBuilderTags.StructureProperties, isDecorator: true }); + await this.plugin.runTask(this.dataState.updateTree(props, { revertOnError: true })); + return props.selector; } /** returns undefined if the component is empty/null */ diff --git a/src/mol-plugin-ui/plugin.tsx b/src/mol-plugin-ui/plugin.tsx index 9ce69a933ef1b4111ed76bbde2acf3ed35472809..cc571085c37fa461ebe77b0b7d6672920022d384 100644 --- a/src/mol-plugin-ui/plugin.tsx +++ b/src/mol-plugin-ui/plugin.tsx @@ -247,7 +247,7 @@ export class CurrentObject extends PluginUIComponent { let decorators: JSX.Element[] | undefined = decoratorChain.length > 1 ? [] : void 0; for (let i = decoratorChain.length - 2; i >= 0; i--) { const d = decoratorChain[i]; - decorators!.push(<ExpandGroup header={d.transform.transformer.definition.display.name}> + decorators!.push(<ExpandGroup key={`${d.transform.transformer.id}-${i}`} header={d.transform.transformer.definition.display.name}> <UpdateTransformControl state={current.state} transform={d.transform} customHeader='none' /> </ExpandGroup>); } diff --git a/src/mol-state/object.ts b/src/mol-state/object.ts index 94e97992435c55872aa9365d5ee1ee670d35ebb0..ec5d712c9e9ec7721aad9776c1aaefdca8da761d 100644 --- a/src/mol-state/object.ts +++ b/src/mol-state/object.ts @@ -146,8 +146,8 @@ export class StateObjectSelector<S extends StateObject = StateObject, T extends /** Create a new build and apply update or use the provided one. */ update(params: StateTransformer.Params<T>, builder?: StateBuilder.Root | StateBuilder.To<any>): StateBuilder - update(params: (old: StateTransformer.Params<T>) => StateTransformer.Params<T>, builder?: StateBuilder.Root | StateBuilder.To<any>): StateBuilder - update(params: ((old: StateTransformer.Params<T>) => StateTransformer.Params<T>) | StateTransformer.Params<T>, builder?: StateBuilder.Root | StateBuilder.To<any>): StateBuilder { + update(params: (old: StateTransformer.Params<T>) => StateTransformer.Params<T> | void, builder?: StateBuilder.Root | StateBuilder.To<any>): StateBuilder + update(params: ((old: StateTransformer.Params<T>) => StateTransformer.Params<T> | void) | StateTransformer.Params<T>, builder?: StateBuilder.Root | StateBuilder.To<any>): StateBuilder { if (!this.state) throw new Error(`To use update() from StateObjectSelector, 'state' must be defined.`); if (!builder) builder = this.state.build(); (builder || this.state.build()).to(this).update(params); diff --git a/src/mol-state/state/builder.ts b/src/mol-state/state/builder.ts index d00baa243cb32810407ca77e6c1c390b242a7ae1..349fe88b5443b56e7fc29fa043b92150d6ab2d69 100644 --- a/src/mol-state/state/builder.ts +++ b/src/mol-state/state/builder.ts @@ -114,13 +114,7 @@ namespace StateBuilder { * Apply the transformed to the parent node * If no params are specified (params <- undefined), default params are lazily resolved. */ - apply<T extends StateTransformer<A, any, any>>(tr: T, params?: StateTransformer.Params<T>, options?: Partial<StateTransform.Options>): To<StateTransformer.To<T>, T> { - if (options?.isDecorator) { - const children = this.state.tree.children.get(this.ref); - if (children.size > 0) throw new Error('Decorators can only be applied to childless nodes.'); - } - - const t = tr.apply(this.ref, params, options); + apply<T extends StateTransformer<A, any, any>>(tr: T, params?: StateTransformer.Params<T>, options?: Partial<StateTransform.Options>): To<StateTransformer.To<T>, T> {const t = tr.apply(this.ref, params, options); this.state.tree.add(t); this.editInfo.count++; this.editInfo.lastUpdate = t.ref; @@ -231,8 +225,8 @@ namespace StateBuilder { } } - update<T extends StateTransformer<any, A, any>>(transformer: T, params: (old: StateTransformer.Params<T>) => StateTransformer.Params<T>): Root - update(params: StateTransformer.Params<T> | ((old: StateTransformer.Params<T>) => StateTransformer.Params<T>)): Root + update<T extends StateTransformer<any, A, any>>(transformer: T, params: (old: StateTransformer.Params<T>) => StateTransformer.Params<T> | void): Root + update(params: StateTransformer.Params<T> | ((old: StateTransformer.Params<T>) => StateTransformer.Params<T> | void)): Root update<T extends StateTransformer<any, A, any>>(paramsOrTransformer: T | any, provider?: (old: StateTransformer.Params<T>) => StateTransformer.Params<T>) { let params: any; if (provider) {