/** * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal */ import { arrayFind } from '../../../mol-data/util'; import { PluginContext } from '../../../mol-plugin/context'; import { StateBuilder, StateObjectRef, StateObjectSelector, StateTransform } from '../../../mol-state'; import { Task } from '../../../mol-task'; import { isProductionMode } from '../../../mol-util/debug'; import { objectForEach } from '../../../mol-util/object'; import { ParamDefinition as PD } from '../../../mol-util/param-definition'; import { createStructureRepresentationParams, StructureRepresentationBuiltInProps, StructureRepresentationProps } from '../../helpers/structure-representation-params'; import { PluginStateObject } from '../../objects'; import { StructureRepresentation3D } from '../../transforms/representation'; import { PresetStructureRepresentations, StructureRepresentationPresetProvider } from './representation-preset'; import { arrayRemoveInPlace } from '../../../mol-util/array'; import { PluginConfig } from '../../../mol-plugin/config'; // TODO factor out code shared with TrajectoryHierarchyBuilder? export type StructureRepresentationPresetProviderRef = keyof PresetStructureRepresentations | StructureRepresentationPresetProvider | string export class StructureRepresentationBuilder { private _providers: StructureRepresentationPresetProvider[] = []; private providerMap: Map = new Map(); private get dataState() { return this.plugin.state.data; } readonly defaultProvider = PresetStructureRepresentations.auto; private resolveProvider(ref: StructureRepresentationPresetProviderRef) { return typeof ref === 'string' ? PresetStructureRepresentations[ref as keyof PresetStructureRepresentations] ?? arrayFind(this._providers, p => p.id === ref) : ref; } hasPreset(s: PluginStateObject.Molecule.Structure) { for (const p of this._providers) { if (!p.isApplicable || p.isApplicable(s, this.plugin)) return true; } return false; } get providers(): ReadonlyArray { return this._providers; } getPresets(s?: PluginStateObject.Molecule.Structure) { if (!s) return this.providers; const ret = []; for (const p of this._providers) { if (p.isApplicable && !p.isApplicable(s, this.plugin)) continue; ret.push(p); } return ret; } getPresetSelect(s?: PluginStateObject.Molecule.Structure): PD.Select { 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, p.display.group]); } return PD.Select('auto', options); } getPresetsWithOptions(s: PluginStateObject.Molecule.Structure) { const options: [string, string][] = []; const map: { [K in string]: PD.Any } = Object.create(null); for (const p of this._providers) { if (p.isApplicable && !p.isApplicable(s, this.plugin)) continue; options.push([p.id, p.display.name]); map[p.id] = p.params ? PD.Group(p.params(s, this.plugin)) : PD.EmptyGroup(); } if (options.length === 0) return PD.MappedStatic('', { '': PD.EmptyGroup() }); return PD.MappedStatic(options[0][0], map, { options }); } registerPreset(provider: StructureRepresentationPresetProvider) { if (this.providerMap.has(provider.id)) { throw new Error(`Representation provider with id '${provider.id}' already registered.`); } this._providers.push(provider); this.providerMap.set(provider.id, provider); } unregisterPreset(provider: StructureRepresentationPresetProvider) { this.providerMap.delete(provider.id); arrayRemoveInPlace(this._providers, provider); } applyPreset(parent: StateObjectRef, preset: K, params?: StructureRepresentationPresetProvider.Params): Promise> | undefined applyPreset

(parent: StateObjectRef, provider: StructureRepresentationPresetProvider, params?: P): Promise | undefined applyPreset(parent: StateObjectRef, providerId: string, params?: any): Promise | undefined applyPreset(parent: StateObjectRef, providerRef: string | StructureRepresentationPresetProvider, params?: any): Promise | undefined { const provider = this.resolveProvider(providerRef); if (!provider) return; const state = this.plugin.state.data; const cell = StateObjectRef.resolveAndCheck(state, parent); if (!cell) { if (!isProductionMode) console.warn(`Applying structure repr. provider to bad cell.`); return; } const pd = provider.params?.(cell.obj, this.plugin) as PD.Params || {}; let prms = params || (provider.params ? PD.getDefaultValues(pd) : {}); const defaults = this.plugin.config.get(PluginConfig.Structure.DefaultRepresentationPresetParams); prms = PD.merge(pd, defaults, prms); const task = Task.create(`${provider.display.name}`, () => provider.apply(cell, prms, this.plugin) as Promise); return this.plugin.runTask(task); } async addRepresentation

(structure: StateObjectRef, props: P, options?: Partial): Promise> async addRepresentation

(structure: StateObjectRef, props: P, options?: Partial): Promise> async addRepresentation(structure: StateObjectRef, props: any, options?: Partial) { const repr = this.dataState.build(); const selector = this.buildRepresentation(repr, structure, props, options); if (!selector) return; await repr.commit(); return selector; } buildRepresentation

(builder: StateBuilder.Root, structure: StateObjectRef | undefined, props: P, options?: Partial): StateObjectSelector buildRepresentation

(builder: StateBuilder.Root, structure: StateObjectRef | undefined, props: P, options?: Partial): StateObjectSelector buildRepresentation(builder: StateBuilder.Root, structure: StateObjectRef | undefined, props: any, options?: Partial) { if (!structure) return; const data = StateObjectRef.resolveAndCheck(this.dataState, structure)?.obj?.data; if (!data) return; const params = createStructureRepresentationParams(this.plugin, data, props); 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) { objectForEach(PresetStructureRepresentations, r => this.registerPreset(r)); } } export namespace StructureRepresentationBuilder { export interface AddRepresentationOptions { initialState?: Partial, tag?: string } }