diff --git a/src/mol-model/volume/data.ts b/src/mol-model/volume/data.ts index cda1035294766b00ea4edc8970c9bee925f1b95f..a5dce1067c2723641f138d791f7da5d61cb2afec 100644 --- a/src/mol-model/volume/data.ts +++ b/src/mol-model/volume/data.ts @@ -21,6 +21,13 @@ interface VolumeData { } namespace VolumeData { + export const Empty: VolumeData = { + cell: SpacegroupCell.Zero, + fractionalBox: Box3D.empty(), + data: Tensor.create(Tensor.Space([0, 0, 0], [0, 1, 2]), Tensor.Data1([])), + dataStats: { min: 0, max: 0, mean: 0, sigma: 0 } + } + const _scale = Mat4.zero(), _translate = Mat4.zero(); export function getGridToCartesianTransform(volume: VolumeData) { const { data: { space } } = volume; diff --git a/src/mol-plugin/behavior/static/state.ts b/src/mol-plugin/behavior/static/state.ts index 420ae299c5cd9b6dec8b003d07c995071436ae86..f3d852d82e218b3cc1455ea579b70bd88032dae4 100644 --- a/src/mol-plugin/behavior/static/state.ts +++ b/src/mol-plugin/behavior/static/state.ts @@ -156,13 +156,5 @@ export function Snapshots(ctx: PluginContext) { } catch (e) { ctx.log.error(`Reading JSON state: ${e}`); } - // const element = document.createElement('a'); - // const json = JSON.stringify(ctx.state.getSnapshot(), null, 2); - // element.setAttribute('href', 'data:application/json;charset=utf-8,' + encodeURIComponent(json)); - // element.setAttribute('download', `mol-star_state_${(name || getFormattedTime())}.json`); - // element.style.display = 'none'; - // document.body.appendChild(element); - // element.click(); - // document.body.removeChild(element); }); } \ No newline at end of file diff --git a/src/mol-plugin/state/transforms/model.ts b/src/mol-plugin/state/transforms/model.ts index 72a8d63f13c3e1ff3e77f564728817df78438b63..e0ac192283859f0b5ee745b275407970b16249dd 100644 --- a/src/mol-plugin/state/transforms/model.ts +++ b/src/mol-plugin/state/transforms/model.ts @@ -27,6 +27,11 @@ const TrajectoryFromMmCif = PluginStateTransform.BuiltIn({ from: SO.Format.Cif, to: SO.Molecule.Trajectory, params(a) { + if (!a) { + return { + blockHeader: PD.makeOptional(PD.Text(void 0, { description: 'Header of the block to parse. If none is specifed, the 1st data block in the file is used.' })) + }; + } const { blocks } = a.data; return { blockHeader: PD.makeOptional(PD.Select(blocks[0] && blocks[0].header, blocks.map(b => [b.header, b.header] as [string, string]), { description: 'Header of the block to parse' })) @@ -55,7 +60,12 @@ const ModelFromTrajectory = PluginStateTransform.BuiltIn({ display: { name: 'Model from Trajectory', description: 'Create a molecular structure from the specified model.' }, from: SO.Molecule.Trajectory, to: SO.Molecule.Model, - params: a => ({ modelIndex: PD.Converted(plus1, minus1, PD.Numeric(1, { min: 1, max: a.data.length, step: 1 }, { description: 'Model Index' })) }) + params: a => { + if (!a) { + return { modelIndex: PD.Numeric(0, {}, { description: 'Zero-based index of the model' }) }; + } + return { modelIndex: PD.Converted(plus1, minus1, PD.Numeric(1, { min: 1, max: a.data.length, step: 1 }, { description: 'Model Index' })) } + } })({ isApplicable: a => a.data.length > 0, apply({ a, params }) { @@ -93,6 +103,9 @@ const StructureAssemblyFromModel = PluginStateTransform.BuiltIn({ from: SO.Molecule.Model, to: SO.Molecule.Structure, params(a) { + if (!a) { + return { id: PD.Text('', { label: 'Assembly Id', description: 'Assembly Id. If none specified (undefined or empty string), the asymmetric unit is used.' }) }; + } const model = a.data; const ids = model.symmetry.assemblies.map(a => [a.id, `${a.id}: ${stringToWords(a.details)}`] as [string, string]); if (!ids.length) ids.push(['deposited', 'Deposited']) @@ -104,7 +117,7 @@ const StructureAssemblyFromModel = PluginStateTransform.BuiltIn({ const model = a.data; const id = params.id; const asm = ModelSymmetry.findAssembly(model, id); - if (id !== 'deposited' && !asm) throw new Error(`Assembly '${id}' not found`); + if (!!id && id !== 'deposited' && !asm) throw new Error(`Assembly '${id}' not found`); const base = Structure.ofModel(model); if (!asm) { @@ -179,7 +192,10 @@ const CustomModelProperties = PluginStateTransform.BuiltIn({ display: { name: 'Custom Model Properties' }, from: SO.Molecule.Model, to: SO.Molecule.Model, - params: (a, ctx: PluginContext) => ({ properties: ctx.customModelProperties.getSelect(a.data) }) + params: (a, ctx: PluginContext) => { + if (!a) return { properties: PD.MultiSelect([], [], { description: 'A list of property descriptor ids.' }) }; + return { properties: ctx.customModelProperties.getSelect(a.data) }; + } })({ apply({ a, params }, ctx: PluginContext) { return Task.create('Custom Props', async taskCtx => { diff --git a/src/mol-plugin/state/transforms/representation.ts b/src/mol-plugin/state/transforms/representation.ts index 83c33de0663d184c2fab2fe0576058d226d40320..4ffdfa8face12d2066213a6cd09a32e9c4a339a1 100644 --- a/src/mol-plugin/state/transforms/representation.ts +++ b/src/mol-plugin/state/transforms/representation.ts @@ -56,6 +56,26 @@ const StructureRepresentation3D = PluginStateTransform.BuiltIn({ params: (a, ctx: PluginContext) => { const { registry, themeCtx } = ctx.structureRepresentation const type = registry.get(registry.default.name); + + if (!a) { + return { + type: PD.Mapped<any>( + registry.default.name, + registry.types, + name => PD.Group<any>(registry.get(name).getParams(themeCtx, Structure.Empty))), + colorTheme: PD.Mapped<any>( + type.defaultColorTheme, + themeCtx.colorThemeRegistry.types, + name => PD.Group<any>(themeCtx.colorThemeRegistry.get(name).getParams({ structure: Structure.Empty })) + ), + sizeTheme: PD.Mapped<any>( + type.defaultSizeTheme, + themeCtx.sizeThemeRegistry.types, + name => PD.Group<any>(themeCtx.sizeThemeRegistry.get(name).getParams({ structure: Structure.Empty })) + ) + } + } + const dataCtx = { structure: a.data } return ({ type: PD.Mapped<any>( @@ -163,6 +183,26 @@ const VolumeRepresentation3D = PluginStateTransform.BuiltIn({ params: (a, ctx: PluginContext) => { const { registry, themeCtx } = ctx.volumeRepresentation const type = registry.get(registry.default.name); + + if (!a) { + return { + type: PD.Mapped<any>( + registry.default.name, + registry.types, + name => PD.Group<any>(registry.get(name).getParams(themeCtx, VolumeData.Empty ))), + colorTheme: PD.Mapped<any>( + type.defaultColorTheme, + themeCtx.colorThemeRegistry.types, + name => PD.Group<any>(themeCtx.colorThemeRegistry.get(name).getParams({ volume: VolumeData.Empty })) + ), + sizeTheme: PD.Mapped<any>( + type.defaultSizeTheme, + themeCtx.sizeThemeRegistry.types, + name => PD.Group<any>(themeCtx.sizeThemeRegistry.get(name).getParams({ volume: VolumeData.Empty })) + ) + } + } + const dataCtx = { volume: a.data } return ({ type: PD.Mapped<any>( diff --git a/src/mol-state/transformer.ts b/src/mol-state/transformer.ts index 05663bd8555f5da3b8ddcbaf23c0fd881acd5e74..d5952ee2e00684ea7ee9a17bfa80951ffae9f60a 100644 --- a/src/mol-state/transformer.ts +++ b/src/mol-state/transformer.ts @@ -151,7 +151,8 @@ export namespace Transformer { name: string, from: A | A[], to: B | B[], - params?: PD.For<P> | ((a: StateObject.From<A>, globalCtx: any) => PD.For<P>), + /** The source StateObject can be undefined: used for generating docs. */ + params?: PD.For<P> | ((a: StateObject.From<A> | undefined, globalCtx: any) => PD.For<P>), display?: string | { name: string, description?: string } }