diff --git a/src/mol-plugin/behavior/dynamic/custom-props.ts b/src/mol-plugin/behavior/dynamic/custom-props.ts index 7dc6eefd80de94e5f390d467ee542e56aeb5e85c..d82ae6dacb61b9fa6f4a6a22d30c3ab440808219 100644 --- a/src/mol-plugin/behavior/dynamic/custom-props.ts +++ b/src/mol-plugin/behavior/dynamic/custom-props.ts @@ -25,7 +25,7 @@ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: bo private provider: CustomPropertyRegistry.Provider = { option: [StructureQualityReport.Descriptor.name, 'PDBe Structure Quality Report'], descriptor: StructureQualityReport.Descriptor, - defaultSelected: false, + defaultSelected: this.params.autoAttach, attachableTo: () => true, attach: this.attach } diff --git a/src/mol-plugin/index.ts b/src/mol-plugin/index.ts index 2201de2baf6d9c9a59034c3e437ff6bedcc5769c..6ed4f0f7a06172e3e92d45a126910048785db2ef 100644 --- a/src/mol-plugin/index.ts +++ b/src/mol-plugin/index.ts @@ -36,7 +36,7 @@ const DefaultSpec: PluginSpec = { PluginSpec.Behavior(PluginBehaviors.Representation.SelectLoci), PluginSpec.Behavior(PluginBehaviors.Representation.DefaultLociLabelProvider), PluginSpec.Behavior(PluginBehaviors.Camera.FocusLociOnSelect, { minRadius: 20, extraRadius: 4 }), - PluginSpec.Behavior(PluginBehaviors.CustomProps.PDBeStructureQualityReport, { autoAttach: false }) + PluginSpec.Behavior(PluginBehaviors.CustomProps.PDBeStructureQualityReport, { autoAttach: true }) ] } diff --git a/src/mol-plugin/state/actions/basic.ts b/src/mol-plugin/state/actions/basic.ts index 6768bbd6d8e3e068758d2b21e66fda8b4672cb23..13a43c24e53a214869eb60581a98d43403383bd4 100644 --- a/src/mol-plugin/state/actions/basic.ts +++ b/src/mol-plugin/state/actions/basic.ts @@ -4,25 +4,20 @@ * @author David Sehnal <david.sehnal@gmail.com> */ +import { PluginContext } from 'mol-plugin/context'; +import { StateTree, Transformer } from 'mol-state'; import { StateAction } from 'mol-state/action'; +import { StateSelection } from 'mol-state/state/selection'; +import { StateTreeBuilder } from 'mol-state/tree/builder'; +import { ParamDefinition as PD } from 'mol-util/param-definition'; import { PluginStateObject } from '../objects'; import { StateTransforms } from '../transforms'; -import { ParamDefinition as PD } from 'mol-util/param-definition'; -import { StateSelection } from 'mol-state/state/selection'; -import { CartoonParams } from 'mol-repr/structure/representation/cartoon'; -import { BallAndStickParams } from 'mol-repr/structure/representation/ball-and-stick'; import { Download } from '../transforms/data'; -import { StateTree, Transformer } from 'mol-state'; -import { StateTreeBuilder } from 'mol-state/tree/builder'; -import { PolymerIdColorThemeParams } from 'mol-theme/color/polymer-id'; -import { UniformSizeThemeParams } from 'mol-theme/size/uniform'; -import { ElementSymbolColorThemeParams } from 'mol-theme/color/element-symbol'; -import { PhysicalSizeThemeParams } from 'mol-theme/size/physical'; -import { SpacefillParams } from 'mol-repr/structure/representation/spacefill'; +import { StructureRepresentation3DHelpers } from '../transforms/representation'; // TODO: "structure parser provider" -export { DownloadStructure } +export { DownloadStructure }; type DownloadStructure = typeof DownloadStructure const DownloadStructure = StateAction.build({ from: PluginStateObject.Root, @@ -55,7 +50,7 @@ const DownloadStructure = StateAction.build({ ] }) } -})(({ params, state }) => { +})(({ params, state }, ctx: PluginContext) => { const b = state.build(); const src = params.source; let url: Transformer.Params<Download>; @@ -77,69 +72,55 @@ const DownloadStructure = StateAction.build({ } const data = b.toRoot().apply(StateTransforms.Data.Download, url); - return state.update(createStructureTree(data, params.source.params.supportProps)); + return state.update(createStructureTree(ctx, data, params.source.params.supportProps)); }); export const OpenStructure = StateAction.build({ display: { name: 'Open Structure', description: 'Load a structure from file and create its default Assembly and visual' }, from: PluginStateObject.Root, params: { file: PD.File({ accept: '.cif,.bcif' }) } -})(({ params, state }) => { +})(({ params, state }, ctx: PluginContext) => { const b = state.build(); const data = b.toRoot().apply(StateTransforms.Data.ReadFile, { file: params.file, isBinary: /\.bcif$/i.test(params.file.name) }); - return state.update(createStructureTree(data, false)); + return state.update(createStructureTree(ctx, data, false)); }); -function createStructureTree(b: StateTreeBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, supportProps: boolean): StateTree { +function createStructureTree(ctx: PluginContext, b: StateTreeBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, supportProps: boolean): StateTree { let root = b .apply(StateTransforms.Data.ParseCif) - .apply(StateTransforms.Model.TrajectoryFromMmCif, {}) + .apply(StateTransforms.Model.TrajectoryFromMmCif) .apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 }); if (supportProps) { - // TODO: implement automatic default property assigment in State.update - root = root.apply(StateTransforms.Model.CustomModelProperties, { properties: [] }); + root = root.apply(StateTransforms.Model.CustomModelProperties); } - root = root.apply(StateTransforms.Model.StructureAssemblyFromModel); - - complexRepresentation(root); + const structure = root.apply(StateTransforms.Model.StructureAssemblyFromModel); + complexRepresentation(ctx, structure); return root.getTree(); } -function complexRepresentation(root: StateTreeBuilder.To<PluginStateObject.Molecule.Structure>) { +function complexRepresentation(ctx: PluginContext, root: StateTreeBuilder.To<PluginStateObject.Molecule.Structure>) { root.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }) - .apply(StateTransforms.Representation.StructureRepresentation3D, { - type: { name: 'cartoon', params: PD.getDefaultValues(CartoonParams) }, - colorTheme: { name: 'polymer-id', params: PD.getDefaultValues(PolymerIdColorThemeParams) }, - sizeTheme: { name: 'uniform', params: PD.getDefaultValues(UniformSizeThemeParams) }, - }); + .apply(StateTransforms.Representation.StructureRepresentation3D, + StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'cartoon')); root.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' }) - .apply(StateTransforms.Representation.StructureRepresentation3D, { - type: { name: 'ball-and-stick', params: PD.getDefaultValues(BallAndStickParams) }, - colorTheme: { name: 'element-symbol', params: PD.getDefaultValues(ElementSymbolColorThemeParams) }, - sizeTheme: { name: 'uniform', params: PD.getDefaultValues(UniformSizeThemeParams) }, - }); + .apply(StateTransforms.Representation.StructureRepresentation3D, + StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick')); root.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' }) - .apply(StateTransforms.Representation.StructureRepresentation3D, { - type: { name: 'ball-and-stick', params: { ...PD.getDefaultValues(BallAndStickParams), alpha: 0.51 } }, - colorTheme: { name: 'element-symbol', params: PD.getDefaultValues(ElementSymbolColorThemeParams) }, - sizeTheme: { name: 'uniform', params: PD.getDefaultValues(UniformSizeThemeParams) }, - }) + .apply(StateTransforms.Representation.StructureRepresentation3D, + StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick', { alpha: 0.51 })); root.apply(StateTransforms.Model.StructureComplexElement, { type: 'spheres' }) - .apply(StateTransforms.Representation.StructureRepresentation3D, { - type: { name: 'spacefill', params: { ...PD.getDefaultValues(SpacefillParams) } }, - colorTheme: { name: 'polymer-id', params: PD.getDefaultValues(PolymerIdColorThemeParams) }, - sizeTheme: { name: 'physical', params: PD.getDefaultValues(PhysicalSizeThemeParams) }, - }) + .apply(StateTransforms.Representation.StructureRepresentation3D, + StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'spacefill')); } export const CreateComplexRepresentation = StateAction.build({ display: { name: 'Create Complex', description: 'Split the structure into Sequence/Water/Ligands/... ' }, from: PluginStateObject.Molecule.Structure -})(({ ref, state }) => { +})(({ ref, state }, ctx: PluginContext) => { const root = state.build().to(ref); - complexRepresentation(root); + complexRepresentation(ctx, root); return state.update(root.getTree()); }); diff --git a/src/mol-plugin/state/transforms/representation.ts b/src/mol-plugin/state/transforms/representation.ts index 1c93b7b63b87f2ffbee56f795117bff6b9640ff1..64c66e6c2660f79facd44cd2a60c6618174bf996 100644 --- a/src/mol-plugin/state/transforms/representation.ts +++ b/src/mol-plugin/state/transforms/representation.ts @@ -14,16 +14,31 @@ import { ParamDefinition as PD } from 'mol-util/param-definition'; import { createTheme } from 'mol-theme/theme'; import { BuiltInStructureRepresentationsName } from 'mol-repr/structure/registry'; import { Structure } from 'mol-model/structure'; +import { UnitsMeshParams } from 'mol-repr/structure/units-visual'; export namespace StructureRepresentation3DHelpers { - export function getDefaultParams(ctx: PluginContext, name: BuiltInStructureRepresentationsName, structure: Structure): Transformer.Params<StructureRepresentation3D> { + export function getDefaultParams(ctx: PluginContext, name: BuiltInStructureRepresentationsName, structure: Structure, meshParams?: Partial<PD.Values<UnitsMeshParams>>): Transformer.Params<StructureRepresentation3D> { const type = ctx.structureRepresentation.registry.get(name); const themeDataCtx = { structure }; const colorParams = ctx.structureRepresentation.themeCtx.colorThemeRegistry.get(type.defaultColorTheme).getParams(themeDataCtx); const sizeParams = ctx.structureRepresentation.themeCtx.sizeThemeRegistry.get(type.defaultSizeTheme).getParams(themeDataCtx) return ({ - type: { name, params: type.defaultValues }, + type: { name, params: meshParams ? { ...type.defaultValues, ...meshParams } : type.defaultValues }, + colorTheme: { name: type.defaultColorTheme, params: PD.getDefaultValues(colorParams) }, + sizeTheme: { name: type.defaultSizeTheme, params: PD.getDefaultValues(sizeParams) } + }) + } + + export function getDefaultParamsStatic(ctx: PluginContext, name: BuiltInStructureRepresentationsName, meshParams?: Partial<PD.Values<UnitsMeshParams>>): Transformer.Params<StructureRepresentation3D> { + const type = ctx.structureRepresentation.registry.get(name); + + // TODO: there should be "static default properties" for the themes. + const themeDataCtx = { }; + const colorParams = ctx.structureRepresentation.themeCtx.colorThemeRegistry.get(type.defaultColorTheme).getParams(themeDataCtx); + const sizeParams = ctx.structureRepresentation.themeCtx.sizeThemeRegistry.get(type.defaultSizeTheme).getParams(themeDataCtx) + return ({ + type: { name, params: meshParams ? { ...type.defaultValues, ...meshParams } : type.defaultValues }, colorTheme: { name: type.defaultColorTheme, params: PD.getDefaultValues(colorParams) }, sizeTheme: { name: type.defaultSizeTheme, params: PD.getDefaultValues(sizeParams) } }) diff --git a/src/mol-state/state.ts b/src/mol-state/state.ts index 75f801a682472e0cffe640268c5b49ba8122cbfa..a08f5f0d0d2aa20b28f22e99243c1e65e48becc6 100644 --- a/src/mol-state/state.ts +++ b/src/mol-state/state.ts @@ -18,6 +18,7 @@ import { StateActionManager } from './action/manager'; import { TransientTree } from './tree/transient'; import { LogEntry } from 'mol-util/log-entry'; import { now, formatTimespan } from 'mol-util/now'; +import { ParamDefinition } from 'mol-util/param-definition'; export { State } @@ -498,6 +499,16 @@ async function updateSubtree(ctx: UpdateContext, root: Ref) { } } +function resolveDefaultParams(ctx: UpdateContext, transform: Transform, src: StateObject) { + const prms = transform.transformer.definition.params; + const defaults = prms + ? ParamDefinition.getDefaultValues(prms(src, ctx.parent.globalContext)) + : { }; + // TODO: this should probably be resolved each time separately the transform is applied. + // the params should be cached in the cell? + (transform.params as any) = defaults; +} + async function updateNode(ctx: UpdateContext, currentRef: Ref): Promise<UpdateNodeResult> { const { oldTree, tree } = ctx; const current = ctx.cells.get(currentRef)!; @@ -514,6 +525,10 @@ async function updateNode(ctx: UpdateContext, currentRef: Ref): Promise<UpdateNo const parent = parentCell.obj!; current.sourceRef = parentCell.transform.ref; + if (!transform.params) { + resolveDefaultParams(ctx, transform, parent); + } + if (!oldTree.transforms.has(currentRef)) { const obj = await createObject(ctx, currentRef, transform.transformer, parent, transform.params); current.obj = obj; diff --git a/src/mol-state/transform.ts b/src/mol-state/transform.ts index dc290b1d3655d3d32ea73a9a74616af47f765356..6637949f202f7260a609bfecbb42153606eed6f6 100644 --- a/src/mol-state/transform.ts +++ b/src/mol-state/transform.ts @@ -40,7 +40,7 @@ export namespace Transform { transformer, props: (options && options.props) || { }, ref, - params: params || {} as any, + params: params as any, version: UUID.create22() } } diff --git a/src/mol-state/tree/builder.ts b/src/mol-state/tree/builder.ts index fcca72f35476165fe593bcd5e2f6c75623a81ade..2eb22efd86b5797c1845f75603646ca94af8ff79 100644 --- a/src/mol-state/tree/builder.ts +++ b/src/mol-state/tree/builder.ts @@ -51,6 +51,12 @@ namespace StateTreeBuilder { export class To<A extends StateObject> implements StateTreeBuilder { get editInfo() { return this.state.editInfo; } + readonly ref: Transform.Ref; + + /** + * Apply the transformed to the parent node + * If no params are specified (params <- undefined), default params are lazily resolved. + */ apply<T extends Transformer<A, any, any>>(tr: T, params?: Transformer.Params<T>, options?: Partial<Transform.Options>, initialCellState?: Partial<StateObjectCell.State>): To<Transformer.To<T>> { const t = tr.apply(this.ref, params, options); this.state.tree.add(t, initialCellState); @@ -84,7 +90,8 @@ namespace StateTreeBuilder { getTree(): StateTree { return this.state.tree.asImmutable(); } - constructor(private state: State, private ref: Transform.Ref, private root: Root) { + constructor(private state: State, ref: Transform.Ref, private root: Root) { + this.ref = ref; if (!this.state.tree.transforms.has(ref)) { throw new Error(`Could not find node '${ref}'.`); }