diff --git a/src/apps/state-docs/pd-to-md.ts b/src/apps/state-docs/pd-to-md.ts index e26624b76b04053048eadd8f82f78522cda6dcf3..13e876ed7789f2705de7572823793dd7f4992e58 100644 --- a/src/apps/state-docs/pd-to-md.ts +++ b/src/apps/state-docs/pd-to-md.ts @@ -39,7 +39,7 @@ function paramInfo(param: PD.Any, offset: number): string { } } -function oToS(options: readonly (readonly [string, string] | readonly [string, string, string])[]) { +function oToS(options: readonly (readonly [string, string] | readonly [string, string, string | undefined])[]) { return options.map(o => `'${o[0]}'`).join(', '); } diff --git a/src/mol-plugin-state/actions/structure.ts b/src/mol-plugin-state/actions/structure.ts index 80da538b4fa6eb50d67744fab4c27500bfb67145..84f21f932de160abb5239158f23b88b6f2d6ef1c 100644 --- a/src/mol-plugin-state/actions/structure.ts +++ b/src/mol-plugin-state/actions/structure.ts @@ -129,7 +129,7 @@ export const DcdProvider: DataFormatProvider<any> = { const DownloadModelRepresentationOptions = (plugin: PluginContext) => PD.Group({ type: RootStructureDefinition.getParams(void 0, 'auto').type, representation: PD.Select(PresetStructureRepresentations.auto.id, - plugin.builders.structure.representation.getPresets().map(p => [p.id, p.display.name] as any), + plugin.builders.structure.representation.getPresets().map(p => [p.id, p.display.name, p.display.group] as any), { description: 'Which representation preset to use.' }), asTrajectory: PD.Optional(PD.Boolean(false, { description: 'Load all entries into a single trajectory.' })) }, { isExpanded: false }); diff --git a/src/mol-plugin-state/builder/preset-provider.ts b/src/mol-plugin-state/builder/preset-provider.ts index 5893d41861bf34b1a49e0c2b80ba3f0cf7316a9c..23993590f22e235e47bf4d983bf4ee384bfbf165 100644 --- a/src/mol-plugin-state/builder/preset-provider.ts +++ b/src/mol-plugin-state/builder/preset-provider.ts @@ -10,7 +10,7 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition'; export interface PresetProvider<O extends StateObject = StateObject, P = any, S = {}> { id: string, - display: { name: string, group: string, description?: string }, + display: { name: string, group?: string, description?: string }, isApplicable?(a: O, plugin: PluginContext): boolean, params?(a: O | undefined, plugin: PluginContext): PD.For<P>, apply(a: StateObjectRef<O>, params: P, plugin: PluginContext): Promise<S> | S diff --git a/src/mol-plugin-state/builder/structure.ts b/src/mol-plugin-state/builder/structure.ts index fc2161ff118b3b9159e0c84127f22278fae3acde..0e7301343b287f31920853e626652d9b345cb6ca 100644 --- a/src/mol-plugin-state/builder/structure.ts +++ b/src/mol-plugin-state/builder/structure.ts @@ -118,7 +118,7 @@ export class StructureBuilder { } /** returns undefined if the component is empty/null */ - async tryCreateComponent(structure: StateObjectRef<SO.Molecule.Structure>, params: StructureComponentParams, key: string, tags?: string[]): Promise<StateObjectRef<SO.Molecule.Structure> | undefined> { + async tryCreateComponent(structure: StateObjectRef<SO.Molecule.Structure>, params: StructureComponentParams, key: string, tags?: string[]): Promise<StateObjectSelector<SO.Molecule.Structure> | undefined> { const state = this.dataState; const root = state.build().to(structure); @@ -157,7 +157,7 @@ export class StructureBuilder { }, key, params?.tags); } - tryCreateComponentFromSelection(structure: StateObjectRef<SO.Molecule.Structure>, selection: StructureSelectionQuery, key: string, params?: { label?: string, tags?: string[] }): Promise<StateObjectRef<SO.Molecule.Structure> | undefined> { + tryCreateComponentFromSelection(structure: StateObjectRef<SO.Molecule.Structure>, selection: StructureSelectionQuery, key: string, params?: { label?: string, tags?: string[] }): Promise<StateObjectSelector<SO.Molecule.Structure> | undefined> { return this.plugin.runTask(Task.create('Query Component', async taskCtx => { let { label, tags } = params || { }; label = (label || '').trim(); diff --git a/src/mol-plugin-state/builder/structure/representation-preset.ts b/src/mol-plugin-state/builder/structure/representation-preset.ts index 7de5d125693408d5dc497b5c31b284e1c8a22404..413dd54859689c75b21b45ad16ca0aafcadedf84 100644 --- a/src/mol-plugin-state/builder/structure/representation-preset.ts +++ b/src/mol-plugin-state/builder/structure/representation-preset.ts @@ -12,15 +12,19 @@ import { VisualQuality, VisualQualityOptions } from '../../../mol-geo/geometry/b import { ColorTheme } from '../../../mol-theme/color'; import { Structure } from '../../../mol-model/structure'; import { PluginContext } from '../../../mol-plugin/context'; -import { StateObjectRef } from '../../../mol-state'; +import { StateObjectRef, StateObjectSelector } from '../../../mol-state'; import { StaticStructureComponentType } from '../../helpers/structure-component'; import { StructureSelectionQueries as Q } from '../../helpers/structure-selection-query'; -export interface StructureRepresentationPresetProvider<P = any, S = {}> extends PresetProvider<PluginStateObject.Molecule.Structure, P, S> { } -export function StructureRepresentationPresetProvider<P, S>(repr: StructureRepresentationPresetProvider<P, S>) { return repr; } +export interface StructureRepresentationPresetProvider<P = any, S extends _Result = _Result> extends PresetProvider<PluginStateObject.Molecule.Structure, P, S> { } +export function StructureRepresentationPresetProvider<P, S extends _Result>(repr: StructureRepresentationPresetProvider<P, S>) { return repr; } export namespace StructureRepresentationPresetProvider { export type Params<P extends StructureRepresentationPresetProvider> = P extends StructureRepresentationPresetProvider<infer T> ? T : never; export type State<P extends StructureRepresentationPresetProvider> = P extends StructureRepresentationPresetProvider<infer _, infer S> ? S : never; + export type Result = { + components?: { [name: string]: StateObjectSelector | undefined }, + representations?: { [name: string]: StateObjectSelector | undefined } + } export const CommonParams = { ignoreHydrogens: PD.Optional(PD.Boolean(false)), @@ -44,13 +48,15 @@ export namespace StructureRepresentationPresetProvider { } } +type _Result = StructureRepresentationPresetProvider.Result + const CommonParams = StructureRepresentationPresetProvider.CommonParams type CommonParams = StructureRepresentationPresetProvider.CommonParams const reprBuilder = StructureRepresentationPresetProvider.reprBuilder const auto = StructureRepresentationPresetProvider({ id: 'preset-structure-representation-auto', - display: { name: 'Automatic', group: 'Preset' }, + display: { name: 'Automatic' }, params: () => CommonParams, apply(ref, params, plugin) { const structure = StateObjectRef.resolveAndCheck(plugin.state.data, ref)?.obj?.data; @@ -73,15 +79,17 @@ const auto = StructureRepresentationPresetProvider({ const empty = StructureRepresentationPresetProvider({ id: 'preset-structure-representation-empty', - display: { name: 'Empty', group: 'Preset' }, + display: { name: 'Empty' }, async apply(ref, params, plugin) { - return { }; + return { }; } }); +const BuiltInPresetGroupName = 'Basic' + const polymerAndLigand = StructureRepresentationPresetProvider({ id: 'preset-structure-representation-polymer-and-ligand', - display: { name: 'Polymer & Ligand', group: 'Preset' }, + display: { name: 'Polymer & Ligand', group: BuiltInPresetGroupName }, params: () => CommonParams, async apply(ref, params, plugin) { const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref); @@ -98,15 +106,13 @@ const polymerAndLigand = StructureRepresentationPresetProvider({ const { update, builder, typeParams, color } = reprBuilder(plugin, params); const representations = { - polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams, color }), - ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams, color }), - nonStandard: builder.buildRepresentation(update, components.nonStandard, { type: 'ball-and-stick', typeParams, color: color || 'polymer-id' }), - branched: components.branched && { - ballAndStick: builder.buildRepresentation(update, components.branched, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.15 }, color }), - snfg3d: builder.buildRepresentation(update, components.branched, { type: 'carbohydrate', typeParams, color }), - }, - water: builder.buildRepresentation(update, components.water, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.51 }, color }), - coarse: builder.buildRepresentation(update, components.coarse, { type: 'spacefill', typeParams, color: color || 'polymer-id' }) + polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams, color }, { tag: 'polymer' }), + ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams, color }, { tag: 'ligand' }), + nonStandard: builder.buildRepresentation(update, components.nonStandard, { type: 'ball-and-stick', typeParams, color: color || 'polymer-id' }, { tag: 'non-standard' }), + branchedBallAndStick: builder.buildRepresentation(update, components.branched, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.15 }, color }, { tag: 'branched-ball-and-stick' }), + branchedSnfg3d: builder.buildRepresentation(update, components.branched, { type: 'carbohydrate', typeParams, color }, { tag: 'branched-snfg-3d' }), + water: builder.buildRepresentation(update, components.water, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.51 }, color }, { tag: 'water' }), + coarse: builder.buildRepresentation(update, components.coarse, { type: 'spacefill', typeParams, color: color || 'polymer-id' }, { tag: 'coarse' }) }; await plugin.updateDataState(update, { revertOnError: false }); @@ -116,7 +122,7 @@ const polymerAndLigand = StructureRepresentationPresetProvider({ const proteinAndNucleic = StructureRepresentationPresetProvider({ id: 'preset-structure-representation-protein-and-nucleic', - display: { name: 'Protein & Nucleic', group: 'Preset' }, + display: { name: 'Protein & Nucleic', group: BuiltInPresetGroupName }, params: () => CommonParams, async apply(ref, params, plugin) { const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref); @@ -129,8 +135,8 @@ const proteinAndNucleic = StructureRepresentationPresetProvider({ const { update, builder, typeParams, color } = reprBuilder(plugin, params); const representations = { - protein: builder.buildRepresentation(update, components.protein, { type: 'cartoon', typeParams, color }), - nucleic: builder.buildRepresentation(update, components.nucleic, { type: 'gaussian-surface', typeParams, color }) + protein: builder.buildRepresentation(update, components.protein, { type: 'cartoon', typeParams, color }, { tag: 'protein' }), + nucleic: builder.buildRepresentation(update, components.nucleic, { type: 'gaussian-surface', typeParams, color }, { tag: 'nucleic' }) }; await plugin.updateDataState(update, { revertOnError: true }); @@ -140,7 +146,7 @@ const proteinAndNucleic = StructureRepresentationPresetProvider({ const coarseSurface = StructureRepresentationPresetProvider({ id: 'preset-structure-representation-coarse-surface', - display: { name: 'Coarse Surface', group: 'Preset' }, + display: { name: 'Coarse Surface', group: BuiltInPresetGroupName }, params: () => CommonParams, async apply(ref, params, plugin) { const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref); @@ -152,26 +158,29 @@ const coarseSurface = StructureRepresentationPresetProvider({ const gaussianProps = Object.create(null); const components = Object.create(null); + let selectionType = 'polymer' + if (size === Structure.Size.Gigantic) { Object.assign(gaussianProps, { radiusOffset: 1, smoothness: 0.5, visuals: ['structure-gaussian-surface-mesh'] }) + selectionType = 'trace' components.trace = await presetSelectionComponent(plugin, structureCell, 'trace') } else if(size === Structure.Size.Huge) { Object.assign(gaussianProps, { smoothness: 0.5, }) - components.trace = await presetSelectionComponent(plugin, structureCell, 'polymer') + components.trace = await presetStaticComponent(plugin, structureCell, 'polymer') } else { - components.trace = await presetSelectionComponent(plugin, structureCell, 'polymer') + components.trace = await presetStaticComponent(plugin, structureCell, 'polymer') } const { update, builder, typeParams, color } = reprBuilder(plugin, params); const representations = { - trace: builder.buildRepresentation(update, components.trace, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color }) + trace: builder.buildRepresentation(update, components.trace, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color }, { tag: selectionType }) }; await plugin.updateDataState(update, { revertOnError: true }); @@ -181,19 +190,19 @@ const coarseSurface = StructureRepresentationPresetProvider({ const polymerCartoon = StructureRepresentationPresetProvider({ id: 'preset-structure-representation-polymer-cartoon', - display: { name: 'Polymer Cartoon', group: 'Preset' }, + display: { name: 'Polymer Cartoon', group: BuiltInPresetGroupName }, params: () => CommonParams, async apply(ref, params, plugin) { const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref); if (!structureCell) return {}; const components = { - polymer: await presetSelectionComponent(plugin, structureCell, 'polymer'), + polymer: await presetStaticComponent(plugin, structureCell, 'polymer'), }; const { update, builder, typeParams, color } = reprBuilder(plugin, params); const representations = { - polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams, color }) + polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams, color }, { tag: 'polymer' }) }; await plugin.updateDataState(update, { revertOnError: true }); @@ -203,19 +212,19 @@ const polymerCartoon = StructureRepresentationPresetProvider({ const atomicDetail = StructureRepresentationPresetProvider({ id: 'preset-structure-representation-atomic-detail', - display: { name: 'Atomic Detail', group: 'Preset' }, + display: { name: 'Atomic Detail', group: BuiltInPresetGroupName }, params: () => CommonParams, async apply(ref, params, plugin) { const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref); if (!structureCell) return {}; const components = { - all: await presetSelectionComponent(plugin, structureCell, 'all'), + all: await presetStaticComponent(plugin, structureCell, 'all'), }; const { update, builder, typeParams, color } = reprBuilder(plugin, params); const representations = { - all: builder.buildRepresentation(update, components.all, { type: 'ball-and-stick', typeParams, color }) + all: builder.buildRepresentation(update, components.all, { type: 'ball-and-stick', typeParams, color }, { tag: 'all' }) }; await plugin.updateDataState(update, { revertOnError: true }); diff --git a/src/mol-plugin-state/builder/structure/representation.ts b/src/mol-plugin-state/builder/structure/representation.ts index 1ab82c5f775193eef9e70c58628d28e13985c9b5..7bde496a8f0d5c61a776c63ef2904313185a3782 100644 --- a/src/mol-plugin-state/builder/structure/representation.ts +++ b/src/mol-plugin-state/builder/structure/representation.ts @@ -54,10 +54,10 @@ export class StructureRepresentationBuilder { } getPresetSelect(s?: PluginStateObject.Molecule.Structure): PD.Select<string> { - const options: [string, string][] = []; + const options: [string, string, string | undefined][] = []; for (const p of this._providers) { if (s && p.isApplicable && !p.isApplicable(s, this.plugin)) continue; - options.push([p.id, p.display.name]); + options.push([p.id, p.display.name, p.display.group]); } return PD.Select('auto', options); } @@ -121,16 +121,17 @@ export class StructureRepresentationBuilder { return selector; } - async buildRepresentation<P extends StructureRepresentationBuiltInProps>(builder: StateBuilder.Root, structure: StateObjectRef<PluginStateObject.Molecule.Structure> | undefined, props: P, options?: Partial<StructureRepresentationBuilder.AddRepresentationOptions>): Promise<StateObjectSelector<PluginStateObject.Molecule.Structure.Representation3D>> - async buildRepresentation<P extends StructureRepresentationProps>(builder: StateBuilder.Root, structure: StateObjectRef<PluginStateObject.Molecule.Structure> | undefined, props: P, options?: Partial<StructureRepresentationBuilder.AddRepresentationOptions>): Promise<StateObjectSelector<PluginStateObject.Molecule.Structure.Representation3D>> - async buildRepresentation(builder: StateBuilder.Root, structure: StateObjectRef<PluginStateObject.Molecule.Structure> | undefined, props: any, options?: Partial<StructureRepresentationBuilder.AddRepresentationOptions>) { + buildRepresentation<P extends StructureRepresentationBuiltInProps>(builder: StateBuilder.Root, structure: StateObjectRef<PluginStateObject.Molecule.Structure> | undefined, props: P, options?: Partial<StructureRepresentationBuilder.AddRepresentationOptions>): StateObjectSelector<PluginStateObject.Molecule.Structure.Representation3D> + buildRepresentation<P extends StructureRepresentationProps>(builder: StateBuilder.Root, structure: StateObjectRef<PluginStateObject.Molecule.Structure> | undefined, props: P, options?: Partial<StructureRepresentationBuilder.AddRepresentationOptions>): StateObjectSelector<PluginStateObject.Molecule.Structure.Representation3D> + buildRepresentation(builder: StateBuilder.Root, structure: StateObjectRef<PluginStateObject.Molecule.Structure> | undefined, props: any, options?: Partial<StructureRepresentationBuilder.AddRepresentationOptions>) { if (!structure) return; const data = StateObjectRef.resolveAndCheck(this.dataState, structure)?.obj?.data; if (!data) return; const params = createStructureRepresentationParams(this.plugin, data, props); - return builder.to(structure) - .apply(StructureRepresentation3D, params, { tags: options?.tag, state: options?.initialState }).selector; + return options?.tag + ? builder.to(structure).applyOrUpdateTagged(options.tag, StructureRepresentation3D, params, { state: options?.initialState }).selector + : builder.to(structure).apply(StructureRepresentation3D, params, { state: options?.initialState }).selector; } constructor(public plugin: PluginContext) { diff --git a/src/mol-plugin-state/manager/structure/component.ts b/src/mol-plugin-state/manager/structure/component.ts index a6580a8c46a26db2342f80d37e1ca5696fcd4c60..d3dbb43ce2f5c650270b29acc2f16a83adc71a02 100644 --- a/src/mol-plugin-state/manager/structure/component.ts +++ b/src/mol-plugin-state/manager/structure/component.ts @@ -25,6 +25,7 @@ import { HierarchyRef, StructureComponentRef, StructureRef, StructureRepresentat import { createStructureColorThemeParams, createStructureSizeThemeParams } from '../../helpers/structure-representation-params'; import { ColorTheme } from '../../../mol-theme/color'; import { SizeTheme } from '../../../mol-theme/size'; +import { objectForEach } from '../../../mol-util/object'; export { StructureComponentManager }; @@ -103,15 +104,52 @@ class StructureComponentManager extends StatefulPluginComponent<StructureCompone } } - applyPreset<P = any, S = {}>(structures: ReadonlyArray<StructureRef>, provider: StructureRepresentationPresetProvider<P, S>, params?: P): Promise<any> { + applyPreset<P extends StructureRepresentationPresetProvider>(structures: ReadonlyArray<StructureRef>, provider: P, params?: StructureRepresentationPresetProvider.Params<P>): Promise<any> { return this.plugin.dataTransaction(async () => { - await this.clearComponents(structures); for (const s of structures) { - await this.plugin.builders.structure.representation.applyPreset(s.cell, provider, params); + const preset = await this.plugin.builders.structure.representation.applyPreset(s.cell, provider, params); + await this.syncPreset(s, preset); } }, { canUndo: 'Preset' }); } + private async syncPreset(root: StructureRef, preset?: StructureRepresentationPresetProvider.Result) { + if (!preset || !preset.components) return this.clearComponents([root]); + + const keptRefs = new Set<string>(); + objectForEach(preset.components, c => { + if (c) keptRefs.add(c.ref); + }); + + if (preset.representations) { + objectForEach(preset.representations, r => { + if (r) keptRefs.add(r.ref); + }); + } + + if (keptRefs.size === 0) return this.clearComponents([root]); + + let changed = false; + const update = this.dataState.build(); + + const sync = (r: HierarchyRef) => { + if (!keptRefs.has(r.cell.transform.ref)) { + changed = true; + update.delete(r.cell); + } + }; + + for (const c of root.components) { + sync(c); + for (const r of c.representations) sync(r); + if (c.genericRepresentations) { + for (const r of c.genericRepresentations) sync(r); + } + } + + if (changed) return this.plugin.updateDataState(update); + } + clear(structures: ReadonlyArray<StructureRef>) { return this.clearComponents(structures); } diff --git a/src/mol-plugin-ui/structure/components.tsx b/src/mol-plugin-ui/structure/components.tsx index 0e33bd70d9802ff3af8d4c0046355457103fbf7d..a9d3ca5e243cb78471cb7db71c228c43e28ae26d 100644 --- a/src/mol-plugin-ui/structure/components.tsx +++ b/src/mol-plugin-ui/structure/components.tsx @@ -90,13 +90,9 @@ class ComponentEditorControls extends PurePluginUIComponent<{}, ComponentEditorC } get presetActions() { - const actions = []; const pivot = this.plugin.managers.structure.component.pivotStructure; - const providers = this.plugin.builders.structure.representation.getPresets(pivot?.cell.obj) - for (const p of providers) { - actions.push(ActionMenu.Item(p.display.name, p)); - } - return actions; + const providers = this.plugin.builders.structure.representation.getPresets(pivot?.cell.obj); + return ActionMenu.createItems(providers, { label: p => p.display.name, category: p => p.display.group }); } applyPreset: ActionMenu.OnSelect = item => { diff --git a/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts b/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts index 92e7a421a531da411f611809f718a38517f4d1ab..69290fb603eeb285f209b150385d1f83373f5bbd 100644 --- a/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts +++ b/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts @@ -146,16 +146,17 @@ const AssemblySymmetry3D = PluginStateTransform.BuiltIn({ const assemblySymmetryPreset = StructureRepresentationPresetProvider({ id: 'preset-structure-representation-rcsb-assembly-symmetry', - display: { name: 'Assembly Symmetry', group: 'Preset' }, + display: { name: 'Assembly Symmetry', group: 'Annotation' }, params: () => StructureRepresentationPresetProvider.CommonParams, async apply(ref, params, plugin) { const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref); - const model = structureCell?.obj?.data.model + const model = structureCell?.obj?.data.model; if (!structureCell || !model) return {}; - await tryCreateAssemblySymmetry(plugin, structureCell) + const assemblySymmetry = await tryCreateAssemblySymmetry(plugin, structureCell); + const repr = await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: Tag.Cluster as any }, plugin); - return await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: Tag.Cluster as any }, plugin) + return { components: repr.components, representations: { ...repr.components, assemblySymmetry } }; } }); diff --git a/src/mol-plugin/behavior/dynamic/custom-props/rcsb/validation-report.ts b/src/mol-plugin/behavior/dynamic/custom-props/rcsb/validation-report.ts index 880e3cee168499d74787d62624d81afb508e000d..0f2b9ff6882af83fd8a37e0d2a03ddb11effe980 100644 --- a/src/mol-plugin/behavior/dynamic/custom-props/rcsb/validation-report.ts +++ b/src/mol-plugin/behavior/dynamic/custom-props/rcsb/validation-report.ts @@ -290,7 +290,7 @@ const hasClash = StructureSelectionQuery('Residues with Clashes', MS.struct.modi const validationReportPreset = StructureRepresentationPresetProvider({ id: 'preset-structure-representation-rcsb-validation-report', - display: { name: 'Validation Report', group: 'Preset' }, + display: { name: 'Validation Report', group: 'Annotation' }, params: () => StructureRepresentationPresetProvider.CommonParams, async apply(ref, params, plugin) { const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref); @@ -305,17 +305,16 @@ const validationReportPreset = StructureRepresentationPresetProvider({ const { components, representations } = await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: colorTheme }, plugin) - components.clashes = await plugin.builders.structure.tryCreateComponentFromExpression(structureCell, hasClash.expression, 'clashes', { label: 'Clashes' }) + const clashes = await plugin.builders.structure.tryCreateComponentFromExpression(structureCell, hasClash.expression, 'clashes', { label: 'Clashes' }) const { update, builder, typeParams, color } = StructureRepresentationPresetProvider.reprBuilder(plugin, params); + let clashesBallAndStick, clashesSnfg3d; if (representations) { - (representations as any).clashes = components.clashes && { - ballAndStick: builder.buildRepresentation(update, components.clashes, { type: 'ball-and-stick', typeParams, color: colorTheme }), - snfg3d: builder.buildRepresentation<any>(update, components.clashes, { type: ClashesRepresentationProvider.name, typeParams, color }), - } + clashesBallAndStick = builder.buildRepresentation(update, components.clashes, { type: 'ball-and-stick', typeParams, color: colorTheme }, { tag: 'clashes-ball-and-stick' }); + clashesSnfg3d = builder.buildRepresentation<any>(update, components.clashes, { type: ClashesRepresentationProvider.name, typeParams, color }, { tag: 'clashes-snfg-3d' }); } await plugin.updateDataState(update, { revertOnError: false }); - return { components, representations }; + return { components: { ...components, clashes }, representations: { ...representations, clashesBallAndStick, clashesSnfg3d } }; } }); \ No newline at end of file diff --git a/src/mol-util/param-definition.ts b/src/mol-util/param-definition.ts index 0784933c1faaf46e7fd0745a1f7593c288d0c970..67151e7128b3dec2204ccd30f8d75dd32eb87b52 100644 --- a/src/mol-util/param-definition.ts +++ b/src/mol-util/param-definition.ts @@ -74,10 +74,10 @@ export namespace ParamDefinition { export interface Select<T> extends Base<T> { type: 'select' /** array of (value, label) tuples */ - options: readonly (readonly [T, string] | readonly [T, string, string])[] + options: readonly (readonly [T, string] | readonly [T, string, string | undefined])[] cycle?: boolean } - export function Select<T>(defaultValue: T, options: readonly (readonly [T, string] | readonly [T, string, string])[], info?: Info & { cycle?: boolean }): Select<T> { + export function Select<T>(defaultValue: T, options: readonly (readonly [T, string] | readonly [T, string, string | undefined])[], info?: Info & { cycle?: boolean }): Select<T> { return setInfo<Select<T>>({ type: 'select', defaultValue: checkDefaultKey(defaultValue, options), options, cycle: info?.cycle }, info) } @@ -469,7 +469,7 @@ export namespace ParamDefinition { return ret; } - function checkDefaultKey<T>(k: T, options: readonly (readonly [T, string] | readonly [T, string, string])[]) { + function checkDefaultKey<T>(k: T, options: readonly (readonly [T, string] | readonly [T, string, string | undefined])[]) { for (const o of options) { if (o[0] === k) return k; }