Browse Source

wip structure tools

David Sehnal 5 years ago
parent
commit
fe96172fcd

+ 5 - 5
src/mol-plugin-state/builder/structure.ts

@@ -5,7 +5,7 @@
  */
 
 import { PluginContext } from '../../mol-plugin/context';
-import { StateObjectRef, StateObjectSelector, StateTransformer } from '../../mol-state';
+import { StateObjectRef, StateObjectSelector, StateTransformer, StateTransform } from '../../mol-state';
 import { PluginStateObject as SO } from '../objects';
 import { StateTransforms } from '../transforms';
 import { RootStructureDefinition } from '../helpers/root-structure';
@@ -95,10 +95,10 @@ export class StructureBuilder {
         }
     }
 
-    async createModel(trajectory: StateObjectRef<SO.Molecule.Trajectory>, params?: StateTransformer.Params<StateTransforms['Model']['ModelFromTrajectory']>) {
+    async createModel(trajectory: StateObjectRef<SO.Molecule.Trajectory>, params?: StateTransformer.Params<StateTransforms['Model']['ModelFromTrajectory']>, initialState?: Partial<StateTransform.State>) {
         const state = this.dataState;
         const model = state.build().to(trajectory)
-            .apply(StateTransforms.Model.ModelFromTrajectory, params || { modelIndex: 0 }, { tags: StructureBuilderTags.Model });
+            .apply(StateTransforms.Model.ModelFromTrajectory, params || { modelIndex: 0 }, { tags: StructureBuilderTags.Model, state: initialState });
 
         await this.plugin.runTask(this.dataState.updateTree(model, { revertOnError: true }));
         return model.selector;
@@ -112,10 +112,10 @@ export class StructureBuilder {
         return props.selector;
     }
 
-    async createStructure(model: StateObjectRef<SO.Molecule.Model>, params?: RootStructureDefinition.Params) {
+    async createStructure(model: StateObjectRef<SO.Molecule.Model>, params?: RootStructureDefinition.Params, initialState?: Partial<StateTransform.State>) {
         const state = this.dataState;
         const structure = state.build().to(model)
-            .apply(StateTransforms.Model.StructureFromModel, { type: params || { name: 'assembly', params: { } } }, { tags: StructureBuilderTags.Structure });        
+            .apply(StateTransforms.Model.StructureFromModel, { type: params || { name: 'assembly', params: { } } }, { tags: StructureBuilderTags.Structure, state: initialState });        
 
         await this.plugin.runTask(this.dataState.updateTree(structure, { revertOnError: true }));
         return structure.selector;

+ 19 - 2
src/mol-plugin-state/manager/structure/hierarchy-state.ts

@@ -51,7 +51,8 @@ function TrajectoryRef(cell: StateObjectCell<SO.Molecule.Trajectory>): Trajector
 export interface ModelRef extends RefBase<'model', SO.Molecule.Model> {
     trajectory?: TrajectoryRef,
     properties?: ModelPropertiesRef,
-    structures: StructureRef[]
+    structures: StructureRef[],
+    genericRepresentations?: GenericRepresentationRef[],
 }
 
 function ModelRef(cell: StateObjectCell<SO.Molecule.Model>, trajectory?: TrajectoryRef): ModelRef {
@@ -74,6 +75,7 @@ export interface StructureRef extends RefBase<'structure', SO.Molecule.Structure
         focus?: StructureComponentRef,
         surroundings?: StructureComponentRef,
     },
+    genericRepresentations?: GenericRepresentationRef[],
     // volumeStreaming?: ....
 }
 
@@ -93,6 +95,7 @@ export interface StructureComponentRef extends RefBase<'structure-component', SO
     structure: StructureRef,
     key?: string,
     representations: StructureRepresentationRef[],
+    genericRepresentations?: GenericRepresentationRef[]
 }
 
 function componentKey(cell: StateObjectCell<SO.Molecule.Structure>) {
@@ -112,6 +115,14 @@ function StructureRepresentationRef(cell: StateObjectCell<SO.Molecule.Structure.
     return { kind: 'structure-representation', cell, version: cell.transform.version, component };
 }
 
+export interface GenericRepresentationRef extends RefBase<'generic-representation', SO.Any> {
+    parent: HierarchyRef
+}
+
+function GenericRepresentationRef(cell: StateObjectCell<SO.Molecule.Structure.Representation3D>, parent: HierarchyRef): GenericRepresentationRef {
+    return { kind: 'generic-representation', cell, version: cell.transform.version, parent };
+}
+
 interface BuildState {
     state: State,
     oldHierarchy: StructureHierarchy,
@@ -239,8 +250,14 @@ function _doPreOrder(ctx: VisitorCtx, root: StateTransform) {
         }
     }
 
-    if (!onLeave && state.currentComponent && SO.Molecule.Structure.Representation3D.is(cell.obj)) {
+    if (!onLeave && !cell.state.isGhost && state.currentComponent && SO.Molecule.Structure.Representation3D.is(cell.obj)) {
         createOrUpdateRefList(state, cell, state.currentComponent.representations, StructureRepresentationRef, cell, state.currentComponent);
+    } else if (!cell.state.isGhost && SO.isRepresentation3D(cell.obj)) {
+        const genericTarget = state.currentComponent || state.currentModel || state.currentStructure;
+        if (genericTarget) {
+            if (!genericTarget.genericRepresentations) genericTarget.genericRepresentations = [];
+            genericTarget.genericRepresentations.push(GenericRepresentationRef(cell, genericTarget));
+        }
     }
 
     const children = ctx.tree.children.get(root.ref);

+ 5 - 1
src/mol-plugin-state/manager/structure/hierarchy.ts

@@ -50,6 +50,10 @@ export class StructureHierarchyManager extends PluginComponent<StructureHierarch
         return this._currentSelectionSet;
     }
 
+    get current() {
+        return this.state.current;
+    }
+
     private syncCurrent<T extends HierarchyRef>(hierarchy: StructureHierarchy, current: ReadonlyArray<T>, all: ReadonlyArray<T>): T[] {
         if (current.length === 0) return all.length > 0 ? [all[0]] : [];
 
@@ -123,7 +127,7 @@ export class StructureHierarchyManager extends PluginComponent<StructureHierarch
 
             const tr = trajectory.cell.obj?.data!;
             for (let i = 0; i < tr.length; i++) {
-                const model = await this.plugin.builders.structure.createModel(trajectory.cell, { modelIndex: i });
+                const model = await this.plugin.builders.structure.createModel(trajectory.cell, { modelIndex: i }, { isCollapsed: true });
                 const structure = await this.plugin.builders.structure.createStructure(model, { name: 'deposited', params: { } });
                 await this.plugin.builders.structure.representation.structurePreset(structure, 'auto', { globalThemeName: 'model-index' });
             }

+ 4 - 3
src/mol-plugin-state/transforms/model.ts

@@ -245,10 +245,11 @@ const ModelFromTrajectory = PluginStateTransform.BuiltIn({
 })({
     isApplicable: a => a.data.length > 0,
     apply({ a, params }) {
-        if (params.modelIndex < 0 || params.modelIndex >= a.data.length) throw new Error(`Invalid modelIndex ${params.modelIndex}`);
+        let modelIndex = params.modelIndex % a.data.length;
+        if (modelIndex < 0) modelIndex += a.data.length;
         const model = a.data[params.modelIndex];
-        const label = `Model ${model.modelNum}`
-        const description = a.data.length === 1 ? undefined : `Model ${params.modelIndex + 1} of ${a.data.length}`
+        const label = `Model ${model.modelNum}`;
+        const description = a.data.length === 1 ? undefined : `of ${a.data.length}`;
         return new SO.Molecule.Model(model, { label, description });
     }
 });

+ 2 - 2
src/mol-plugin-ui/controls/common.tsx

@@ -332,14 +332,14 @@ export class ToggleButton extends React.PureComponent<ToggleButtonProps> {
     }
 }
 
-export class ExpandGroup extends React.PureComponent<{ header: string, initiallyExpanded?: boolean, noOffset?: boolean }, { isExpanded: boolean }> {
+export class ExpandGroup extends React.PureComponent<{ header: string, initiallyExpanded?: boolean, noOffset?: boolean, marginTop?: 0 | string }, { isExpanded: boolean }> {
     state = { isExpanded: !!this.props.initiallyExpanded };
 
     toggleExpanded = () => this.setState({ isExpanded: !this.state.isExpanded });
 
     render() {
         return <>
-            <div className='msp-control-group-header' style={{ marginTop: '1px' }}>
+            <div className='msp-control-group-header' style={{ marginTop: this.props.marginTop !== void 0 ? this.props.marginTop : '1px' }}>
                 <button className='msp-btn msp-btn-block' onClick={this.toggleExpanded}>
                     <Icon name={this.state.isExpanded ? 'collapse' : 'expand'} />
                     {this.props.header}

+ 26 - 5
src/mol-plugin-ui/structure/components.tsx

@@ -10,7 +10,7 @@ import { StructureComponentRef, StructureRepresentationRef, StructureRef } from
 import { PluginCommands } from '../../mol-plugin/commands';
 import { State } from '../../mol-state';
 import { ParamDefinition } from '../../mol-util/param-definition';
-import { CollapsableControls, CollapsableState, PurePluginUIComponent } from '../base';
+import { CollapsableControls, CollapsableState, PurePluginUIComponent, PluginUIComponent } from '../base';
 import { ActionMenu } from '../controls/action-menu';
 import { ExpandGroup, IconButton, ToggleButton } from '../controls/common';
 import { Icon } from '../controls/icons';
@@ -183,12 +183,33 @@ class ComponentListControls extends PurePluginUIComponent {
 
     render() {
         const componentGroups = this.plugin.managers.structure.hierarchy.currentComponentGroups;
-        return <div>
+        if (componentGroups.length === 0) return null;
+
+        return <div style={{ marginTop: '6px' }}>
             {componentGroups.map(g => <StructureComponentGroup key={g[0].cell.transform.ref} group={g} />)}
+            <CurrentFocus />
         </div>;
     }
 }
 
+class CurrentFocus extends PluginUIComponent {
+    findInteraction() {
+        const xs = this.plugin.managers.structure.hierarchy.current.structures;
+        for (const s of xs) {
+            if (s.currentFocus?.focus || s.currentFocus?.surroundings) return s.currentFocus;
+        }
+    }
+
+    render() {
+        const interaction = this.findInteraction();
+        if (!interaction) return null;
+        return <ExpandGroup header='Current Focus' marginTop={0} noOffset>
+            {interaction.focus && <StructureComponentGroup group={[interaction.focus]} />}
+            {interaction.surroundings && <StructureComponentGroup group={[interaction.surroundings]} />}
+        </ExpandGroup>;
+    }
+}
+
 type StructureComponentEntryActions = 'action' | 'remove'
 
 class StructureComponentGroup extends PurePluginUIComponent<{ group: StructureComponentRef[] }, { action?: StructureComponentEntryActions }> {
@@ -283,8 +304,8 @@ class StructureComponentGroup extends PurePluginUIComponent<{ group: StructureCo
         const component = this.pivot;
         const cell = component.cell;
         const label = cell.obj?.label;
-        return <>
-            <div className='msp-control-row' style={{ marginTop: '6px' }}>
+        return <div style={{ marginBottom: '6px' }}>
+            <div className='msp-control-row'>
                 <button className='msp-control-button-label' title={`${label}. Click to focus.`} onClick={this.focus} onMouseEnter={this.highlight} onMouseLeave={this.clearHighlight} style={{ textAlign: 'left' }}>
                     {label}
                 </button>
@@ -299,7 +320,7 @@ class StructureComponentGroup extends PurePluginUIComponent<{ group: StructureCo
             <div className='msp-control-offset'>
                 {component.representations.map(r => <StructureRepresentationEntry group={this.props.group} key={r.cell.transform.ref} representation={r} />)}
             </div>
-        </>;
+        </div>;
     }
 }
 

+ 1 - 1
src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts

@@ -81,7 +81,7 @@ export class StructureRepresentationInteractionBehavior extends PluginBehavior.W
         // Selections
         if (!refs[StructureRepresentationInteractionTags.ResidueSel]) {
             refs[StructureRepresentationInteractionTags.ResidueSel] = builder.to(refs['structure-interaction-group']).apply(StateTransforms.Model.StructureSelectionFromBundle,
-                { bundle: { } as any, label: 'Residue' }, { tags: StructureRepresentationInteractionTags.ResidueSel }).ref;
+                { bundle: { } as any, label: 'Focus' }, { tags: StructureRepresentationInteractionTags.ResidueSel }).ref;
         }
 
         if (!refs[StructureRepresentationInteractionTags.SurrSel]) {