123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 |
- /**
- * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
- import { setSubtreeVisibility } from '../../../mol-plugin/behavior/static/state';
- import { PluginCommands } from '../../../mol-plugin/commands';
- import { PluginContext } from '../../../mol-plugin/context';
- import { StateTransform, StateTree } from '../../../mol-state';
- import { SetUtils } from '../../../mol-util/set';
- import { TrajectoryHierarchyPresetProvider } from '../../builder/structure/hierarchy-preset';
- import { PluginComponent } from '../../component';
- import { buildStructureHierarchy, HierarchyRef, ModelRef, StructureComponentRef, StructureHierarchy, StructureRef, TrajectoryRef } from './hierarchy-state';
- export class StructureHierarchyManager extends PluginComponent {
- private state = {
- syncedTree: this.dataState.tree,
- notified: false,
- hierarchy: StructureHierarchy(),
- selection: {
- trajectories: [] as ReadonlyArray<TrajectoryRef>,
- models: [] as ReadonlyArray<ModelRef>,
- structures: [] as ReadonlyArray<StructureRef>
- }
- }
- readonly behaviors = {
- selection: this.ev.behavior({
- hierarchy: this.current,
- trajectories: this.selection.trajectories,
- models: this.selection.models,
- structures: this.selection.structures
- })
- }
- private get dataState() {
- return this.plugin.state.data;
- }
- private _currentComponentGroups: ReturnType<typeof StructureHierarchyManager['getComponentGroups']> | undefined = void 0;
- get currentComponentGroups() {
- if (this._currentComponentGroups) return this._currentComponentGroups;
- this._currentComponentGroups = StructureHierarchyManager.getComponentGroups(this.selection.structures);
- return this._currentComponentGroups;
- }
- private _currentSelectionSet: Set<StateTransform.Ref> | undefined = void 0;
- get seletionSet() {
- if (this._currentSelectionSet) return this._currentSelectionSet;
- this._currentSelectionSet = new Set();
- for (const r of this.selection.trajectories) this._currentSelectionSet.add(r.cell.transform.ref);
- for (const r of this.selection.models) this._currentSelectionSet.add(r.cell.transform.ref);
- for (const r of this.selection.structures) this._currentSelectionSet.add(r.cell.transform.ref);
- return this._currentSelectionSet;
- }
- get current() {
- this.sync(false);
- return this.state.hierarchy;
- }
- get selection() {
- this.sync(false);
- return this.state.selection;
- }
- private syncCurrent<T extends HierarchyRef>(all: ReadonlyArray<T>, added: Set<StateTransform.Ref>): T[] {
- const current = this.seletionSet;
- const newCurrent: T[] = [];
- for (const r of all) {
- const ref = r.cell.transform.ref;
- if (current.has(ref) || added.has(ref)) newCurrent.push(r);
- }
- if (newCurrent.length === 0) return all.length > 0 ? [all[0]] : [];
- return newCurrent;
- }
- private sync(notify: boolean) {
- if (!notify && this.dataState.inUpdate) return;
- if (this.state.syncedTree === this.dataState.tree) {
- if (notify && !this.state.notified) {
- this.state.notified = true;
- this.behaviors.selection.next({ hierarchy: this.state.hierarchy, ...this.state.selection });
- }
- return;
- }
- this.state.syncedTree = this.dataState.tree;
- const update = buildStructureHierarchy(this.plugin.state.data, this.current);
- if (!update.changed) {
- return;
- }
- const { hierarchy } = update;
- const trajectories = this.syncCurrent(hierarchy.trajectories, update.added);
- const models = this.syncCurrent(hierarchy.models, update.added);
- const structures = this.syncCurrent(hierarchy.structures, update.added);
- this._currentComponentGroups = void 0;
- this._currentSelectionSet = void 0;
- this.state.hierarchy = hierarchy;
- this.state.selection.trajectories = trajectories;
- this.state.selection.models = models;
- this.state.selection.structures = structures;
- if (notify) {
- this.state.notified = true;
- this.behaviors.selection.next({ hierarchy, trajectories, models, structures });
- } else {
- this.state.notified = false;
- }
- }
- updateCurrent(refs: HierarchyRef[], action: 'add' | 'remove') {
- const hierarchy = this.current;
- const set = action === 'add'
- ? SetUtils.union(this.seletionSet, new Set(refs.map(r => r.cell.transform.ref)))
- : SetUtils.difference(this.seletionSet, new Set(refs.map(r => r.cell.transform.ref)));
- const trajectories = [];
- const models = [];
- const structures = [];
- for (const t of hierarchy.trajectories) {
- if (set.has(t.cell.transform.ref)) trajectories.push(t);
- }
- for (const m of hierarchy.models) {
- if (set.has(m.cell.transform.ref)) models.push(m);
- }
- for (const s of hierarchy.structures) {
- if (set.has(s.cell.transform.ref)) structures.push(s);
- }
- this._currentComponentGroups = void 0;
- this._currentSelectionSet = void 0;
- this.state.selection.trajectories = trajectories;
- this.state.selection.models = models;
- this.state.selection.structures = structures;
- this.behaviors.selection.next({ hierarchy, trajectories, models, structures });
- }
- remove(refs: (HierarchyRef | string)[], canUndo?: boolean) {
- if (refs.length === 0) return;
- const deletes = this.plugin.state.data.build();
- for (const r of refs) deletes.delete(typeof r === 'string' ? r : r.cell.transform.ref);
- return this.plugin.updateDataState(deletes, { canUndo: canUndo ? 'Remove' : false });
- }
- toggleVisibility(refs: ReadonlyArray<HierarchyRef>, action?: 'show' | 'hide') {
- if (refs.length === 0) return;
- const isHidden = action !== void 0
- ? (action === 'show' ? false : true)
- : !refs[0].cell.state.isHidden;
- for (const c of refs) {
- setSubtreeVisibility(this.dataState, c.cell.transform.ref, isHidden);
- }
- }
- applyPreset<P = any, S = {}>(trajectories: ReadonlyArray<TrajectoryRef>, provider: TrajectoryHierarchyPresetProvider<P, S>, params?: P): Promise<any> {
- return this.plugin.dataTransaction(async () => {
- for (const t of trajectories) {
- if (t.models.length > 0) {
- await this.clearTrajectory(t);
- }
- await this.plugin.builders.structure.hierarchy.applyPreset(t.cell, provider, params);
- }
- });
- }
- async updateStructure(s: StructureRef, params: any) {
- await this.plugin.dataTransaction(async () => {
- const root = StateTree.getDecoratorRoot(this.dataState.tree, s.cell.transform.ref);
- const children = this.dataState.tree.children.get(root).toArray();
- await this.remove(children, false);
- await this.plugin.state.updateTransform(this.plugin.state.data, s.cell.transform.ref, params, 'Structure Type');
- await this.plugin.builders.structure.representation.applyPreset(s.cell.transform.ref, 'auto');
- }, { canUndo: 'Structure Type' });
- PluginCommands.Camera.Reset(this.plugin);
- }
- private clearTrajectory(trajectory: TrajectoryRef) {
- const builder = this.dataState.build();
- for (const m of trajectory.models) {
- builder.delete(m.cell);
- }
- return this.plugin.updateDataState(builder);
- }
- constructor(private plugin: PluginContext) {
- super();
- this.subscribe(plugin.state.data.events.changed, e => {
- if (e.inTransaction || plugin.behaviors.state.isAnimating.value) return;
- this.sync(true);
- });
- this.subscribe(plugin.behaviors.state.isAnimating, isAnimating => {
- if (!isAnimating && !plugin.behaviors.state.isUpdating.value) this.sync(true);
- });
- }
- }
- export namespace StructureHierarchyManager {
- export function getComponentGroups(structures: ReadonlyArray<StructureRef>): StructureComponentRef[][] {
- if (!structures.length) return [];
- if (structures.length === 1) return structures[0].components.map(c => [c]);
- const groups: StructureComponentRef[][] = [];
- const map = new Map<string, StructureComponentRef[]>();
- for (const s of structures) {
- for (const c of s.components) {
- const key = c.key;
- if (!key) continue;
- let component = map.get(key);
- if (!component) {
- component = [];
- map.set(key, component);
- groups.push(component);
- }
- component.push(c);
- }
- }
- return groups;
- }
- export function getSelectedStructuresDescription(plugin: PluginContext) {
- const { structures } = plugin.managers.structure.hierarchy.selection;
- if (structures.length === 0) return '';
- if (structures.length === 1) {
- const s = structures[0];
- const data = s.cell.obj?.data;
- if (!data) return s.cell.obj?.label || 'Structure';
- const model = data.models[0] || data.representativeModel || data.masterModel;
- if (!model) return s.cell.obj?.label || 'Structure';
- const entryId = model.entryId;
- if (s.model?.trajectory?.models && s.model.trajectory.models.length === 1) return entryId;
- if (s.model) return `${s.model.cell.obj?.label} | ${entryId}`;
- return entryId;
- }
- const p = structures[0];
- const t = p?.model?.trajectory;
- let sameTraj = true;
- for (const s of structures) {
- if (s?.model?.trajectory !== t) {
- sameTraj = false;
- break;
- }
- }
- return sameTraj && t ? `${t.cell.obj?.label} | ${structures.length} structures` : `${structures.length} structures`;
- }
- }
|