Browse Source

added open-file action, refactored structure-viewer

Alexander Rose 5 years ago
parent
commit
432bb93d9f

+ 59 - 0
src/structure-viewer/helpers/model.ts

@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { StateElements, StructureViewerState, LoadParams } from '../types';
+import { PluginCommands } from 'molstar/lib/mol-plugin/command';
+import { StateBuilder } from 'molstar/lib/mol-state';
+import { StateTransforms } from 'molstar/lib/mol-plugin/state/transforms';
+import { PluginContext } from 'molstar/lib/mol-plugin/context';
+import { Scheduler } from 'molstar/lib/mol-task';
+import { PluginStateObject as PSO } from 'molstar/lib/mol-plugin/state/objects';
+
+export class ModelLoader {
+    download(b: StateBuilder.To<PSO.Root>, url: string, isBinary: boolean) {
+        return b.apply(StateTransforms.Data.Download, { url, isBinary })
+    }
+
+    readFile(b: StateBuilder.To<PSO.Root>, file: File, isBinary: boolean) {
+        return b.apply(StateTransforms.Data.ReadFile, { file, isBinary })
+    }
+
+    model(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>) {
+        const parsed = b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif, {}, { ref: StateElements.Trajectory })
+
+        return parsed
+            .apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 }, { ref: StateElements.Model })
+    }
+
+    async load({ fileOrUrl, format = 'cif', assemblyId = 'deposited' }: LoadParams) {
+        if (!fileOrUrl) return
+
+        const state = this.plugin.state.dataState;
+        await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state, ref: state.tree.root.ref })
+
+        const isBinary = format === 'bcif'
+        const data = fileOrUrl instanceof File
+            ? this.readFile(state.build().toRoot(), fileOrUrl, isBinary)
+            : this.download(state.build().toRoot(), fileOrUrl, isBinary)
+        const model = this.model(data);
+        await this.applyState(model)
+        await this.init(assemblyId)
+    }
+
+    async init(assemblyId = 'deposited') {
+        await (this.plugin.customState as StructureViewerState).structureView.setAssembly(assemblyId)
+
+        Scheduler.setImmediate(() => PluginCommands.Camera.Reset.dispatch(this.plugin, { }))
+    }
+
+    applyState(tree: StateBuilder) {
+        return PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree })
+    }
+
+    constructor(private plugin: PluginContext) {
+
+    }
+}

+ 112 - 0
src/structure-viewer/helpers/structure.ts

@@ -0,0 +1,112 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { StateElements, AssemblyNames, StructureViewerState } from '../types';
+import { PluginCommands } from 'molstar/lib/mol-plugin/command';
+import { StateBuilder, State } from 'molstar/lib/mol-state';
+import { StateTransforms } from 'molstar/lib/mol-plugin/state/transforms';
+import { Vec3 } from 'molstar/lib/mol-math/linear-algebra';
+import { PluginContext } from 'molstar/lib/mol-plugin/context';
+import { Scheduler } from 'molstar/lib/mol-task';
+
+export class StructureView {
+    applyState(tree: StateBuilder) {
+        return PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
+    }
+
+    get experimentalData () {
+        return (this.plugin.customState as StructureViewerState).volumeData
+    }
+
+    async preset() {
+        await this.plugin.helpers.structureRepresentation.preset()
+        Scheduler.setImmediate(() => PluginCommands.Camera.Reset.dispatch(this.plugin, { }))
+    }
+
+    private ensureModelUnitcell(tree: StateBuilder.Root, state: State) {
+        if (!state.tree.transforms.has(StateElements.ModelUnitcell)) {
+            tree.to(StateElements.Model).apply(
+                StateTransforms.Representation.ModelUnitcell3D,
+                undefined, { ref: StateElements.ModelUnitcell }
+            )
+        }
+    }
+
+    async setAssembly(id: string) {
+        const state = this.plugin.state.dataState;
+        const tree = state.build();
+        if (id === AssemblyNames.Unitcell) {
+            const props = { ijkMin: Vec3.create(0, 0, 0), ijkMax: Vec3.create(0, 0, 0) }
+            tree.delete(StateElements.Assembly)
+                .to(StateElements.Model).apply(
+                    StateTransforms.Model.StructureSymmetryFromModel,
+                    props, { ref: StateElements.Assembly, tags: [ AssemblyNames.Unitcell ] }
+                )
+            this.ensureModelUnitcell(tree, state)
+        } else if (id === AssemblyNames.Supercell) {
+            const props = { ijkMin: Vec3.create(-1, -1, -1), ijkMax: Vec3.create(1, 1, 1) }
+            tree.delete(StateElements.Assembly)
+                .to(StateElements.Model).apply(
+                    StateTransforms.Model.StructureSymmetryFromModel,
+                    props, { ref: StateElements.Assembly, tags: [ AssemblyNames.Supercell ] }
+                )
+            this.ensureModelUnitcell(tree, state)
+        } else if (id === AssemblyNames.CrystalContacts) {
+            const props = { radius: 5 }
+            tree.delete(StateElements.ModelUnitcell)
+            tree.delete(StateElements.Assembly)
+                .to(StateElements.Model).apply(
+                    StateTransforms.Model.StructureSymmetryMatesFromModel,
+                    props, { ref: StateElements.Assembly, tags: [ AssemblyNames.CrystalContacts ] }
+                )
+        } else {
+            tree.delete(StateElements.ModelUnitcell)
+            tree.delete(StateElements.Assembly)
+                .to(StateElements.Model).apply(
+                    StateTransforms.Model.StructureAssemblyFromModel,
+                    { id }, { ref: StateElements.Assembly }
+                )
+        }
+        await this.applyState(tree)
+        await this.preset()
+        await this.experimentalData.init()
+    }
+
+    async setModel(modelIndex: number) {
+        const state = this.plugin.state.dataState;
+        const tree = state.build();
+        if (modelIndex === -1) {
+            tree.delete(StateElements.Model)
+                .to(StateElements.Trajectory).apply(
+                    StateTransforms.Model.StructureFromTrajectory,
+                    {}, { ref: StateElements.Assembly }
+                )
+        } else {
+            if (state.tree.transforms.has(StateElements.Model)) {
+                tree.to(StateElements.Model).update(
+                    StateTransforms.Model.ModelFromTrajectory,
+                    props => ({ ...props, modelIndex })
+                )
+            } else {
+                tree.delete(StateElements.Assembly)
+                    .to(StateElements.Trajectory).apply(
+                        StateTransforms.Model.ModelFromTrajectory,
+                        { modelIndex }, { ref: StateElements.Model }
+                    )
+                    .apply(
+                        StateTransforms.Model.StructureAssemblyFromModel,
+                        { id: AssemblyNames.Deposited }, { ref: StateElements.Assembly }
+                    )
+            }
+        }
+        await this.applyState(tree)
+        await this.preset()
+    }
+
+    constructor(private plugin: PluginContext) {
+
+    }
+}

+ 55 - 0
src/structure-viewer/helpers/volume.ts

@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { StateElements, StructureViewerState } from '../types';
+import { PluginCommands } from 'molstar/lib/mol-plugin/command';
+import { StateSelection } from 'molstar/lib/mol-state';
+import { PluginContext } from 'molstar/lib/mol-plugin/context';
+import { InitVolumeStreaming, CreateVolumeStreamingInfo } from 'molstar/lib/mol-plugin/behavior/dynamic/volume-streaming/transformers';
+import { ParamDefinition as PD } from 'molstar/lib/mol-util/param-definition';
+import { Model } from 'molstar/lib/mol-model/structure';
+
+export class VolumeData {
+    get state() {
+        return this.plugin.state.dataState;
+    }
+
+    async init() {
+        const { props } = (this.plugin.customState as StructureViewerState)
+        const model = this.state.select(StateElements.Model)[0].obj;
+        const asm = this.state.select(StateElements.Assembly)[0].obj;
+        if (!model || !asm) return
+
+        const m = model.data as Model
+        const d = m.sourceData.data
+        const hasXrayMap = d.pdbx_database_status.status_code_sf.value(0) === 'REL'
+        let hasEmMap = false
+        for (let i = 0, il = d.pdbx_database_related._rowCount; i < il; ++i) {
+            if (d.pdbx_database_related.db_name.value(i).toUpperCase() === 'EMDB') {
+                hasEmMap = true
+                break
+            }
+        }
+
+        if (hasXrayMap || hasEmMap) {
+            const params = PD.getDefaultValues(InitVolumeStreaming.definition.params!(asm, this.plugin));
+            params.defaultView = 'selection-box';
+            params.options.behaviorRef = StateElements.VolumeStreaming;
+            params.options.serverUrl = props.volumeServerUrl
+            await this.plugin.runTask(this.state.applyAction(InitVolumeStreaming, params, StateElements.Assembly));
+        }
+    }
+
+    async remove() {
+        const r = this.state.select(StateSelection.Generators.ofTransformer(CreateVolumeStreamingInfo))[0];
+        if (!r) return;
+        await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state: this.state, ref: r.transform.ref });
+    }
+
+    constructor(private plugin: PluginContext) {
+
+    }
+}

+ 9 - 8
src/structure-viewer/index.html

@@ -13,10 +13,10 @@
             }
             #app {
                 position: absolute;
-                left: 100px;
+                left: 5%;
                 top: 100px;
-                width: 1024px;
-                height: 800px;
+                min-width: 90%;
+                height: 85%;
             }
 
             .msp-layout-expanded {
@@ -25,8 +25,8 @@
 
             #menu {
                 position: absolute;
-                right: 100px;
-                top: 100px;
+                left: 100px;
+                top: 50px;
             }
 
             #menu > select {
@@ -45,12 +45,13 @@
                 return m ? decodeURIComponent(m[1]) : undefined
             }
 
+            const pdbId = getQueryParam('pdbId');
+            const assemblyId = getQueryParam('assemblyId') || 'deposited';
+
             // create an instance of the plugin
-            var viewer = new app.StructureViewer('app');
+            var viewer = new app.StructureViewer('app', { showOpenFileControls: !pdbId });
 
             // load pdbId
-            const pdbId = getQueryParam('pdbId');
-            const assemblyId = getQueryParam('assemblyId') || 'deposited';
             if (pdbId) viewer.loadPdbId(pdbId, assemblyId);
         </script>
         <div id="menu">

+ 18 - 87
src/structure-viewer/index.ts

@@ -11,25 +11,21 @@ import './favicon.ico'
 import { PluginContext } from 'molstar/lib/mol-plugin/context';
 import { PluginCommands } from 'molstar/lib/mol-plugin/command';
 import { PluginBehaviors } from 'molstar/lib/mol-plugin/behavior';
-import { StateTransforms } from 'molstar/lib/mol-plugin/state/transforms';
-import { PluginStateObject as PSO } from 'molstar/lib/mol-plugin/state/objects';
 import { AnimateModelIndex } from 'molstar/lib/mol-plugin/state/animation/built-in';
-import { StateBuilder, StateSelection } from 'molstar/lib/mol-state';
-import { LoadParams, SupportedFormats, StateElements, StructureViewerState } from './helpers';
+import { SupportedFormats, StructureViewerState, StructureViewerProps } from './types';
 import { ControlsWrapper, ViewportWrapper } from './ui/controls';
-import { Scheduler } from 'molstar/lib/mol-task';
-import { InitVolumeStreaming, CreateVolumeStreamingInfo } from 'molstar/lib/mol-plugin/behavior/dynamic/volume-streaming/transformers';
-import { ParamDefinition as PD } from 'molstar/lib/mol-util/param-definition';
 import { PluginSpec } from 'molstar/lib/mol-plugin/spec';
 import { StructureRepresentationInteraction } from 'molstar/lib/mol-plugin/behavior/dynamic/selection/structure-representation-interaction';
-import { Model } from 'molstar/lib/mol-model/structure';
+
 import { ColorNames } from 'molstar/lib/mol-util/color/names';
-import { StructureControlsHelper } from './ui/structure';
+import { StructureView } from './helpers/structure';
 import ReactDOM = require('react-dom');
 import React = require('react');
+import { ModelLoader } from './helpers/model';
+import { VolumeData } from './helpers/volume';
 require('./skin/rcsb.scss')
 
-export const DefaultStructureViewerProps = {
+export const DefaultStructureViewerProps: StructureViewerProps = {
     // volumeServerUrl: 'https://ds.litemol.org/',
     volumeServerUrl: '//alpha-maps.rcsb.org/',
     modelUrlProvider: (pdbId: string) => {
@@ -41,8 +37,8 @@ export const DefaultStructureViewerProps = {
             format: 'bcif' as SupportedFormats
         }
     },
+    showOpenFileControls: false,
 }
-export type StructureViewerProps = typeof DefaultStructureViewerProps
 
 export class StructureViewer {
     private readonly plugin: PluginContext;
@@ -51,6 +47,8 @@ export class StructureViewer {
     constructor(target: string | HTMLElement, props: Partial<StructureViewerProps> = {}) {
         target = typeof target === 'string' ? document.getElementById(target)! : target
 
+        this.props = { ...DefaultStructureViewerProps, ...props }
+
         this.plugin = new PluginContext({
             ...DefaultPluginSpec,
             behaviors: [
@@ -73,7 +71,7 @@ export class StructureViewer {
                 initial: {
                     isExpanded: false,
                     showControls: true,
-                    outsideControls: false
+                    controlsDisplay: 'reactive'
                 },
                 controls: {
                     left: 'none',
@@ -85,91 +83,24 @@ export class StructureViewer {
         });
 
         (this.plugin.customState as StructureViewerState) = {
-            structureControlsHelper: new StructureControlsHelper(this.plugin),
-            experimentalData: this.experimentalData
+            props: this.props,
+            modelLoader: new ModelLoader(this.plugin),
+            structureView: new StructureView(this.plugin),
+            volumeData: new VolumeData(this.plugin)
         };
 
-        this.props = { ...DefaultStructureViewerProps, ...props }
-
         ReactDOM.render(React.createElement(Plugin, { plugin: this.plugin }), target)
 
         const renderer = this.plugin.canvas3d.props.renderer;
         PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: ColorNames.white } } });
     }
 
-    get state() {
-        return this.plugin.state.dataState;
-    }
-
-    private download(b: StateBuilder.To<PSO.Root>, url: string, isBinary: boolean) {
-        return b.apply(StateTransforms.Data.Download, { url, isBinary })
-    }
-
-    private model(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats) {
-        const isMmcif = format === 'cif' || format === 'bcif'
-        const parsed = isMmcif
-            ? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif, {}, { ref: StateElements.Trajectory })
-            : b.apply(StateTransforms.Model.TrajectoryFromPDB, {}, { ref: StateElements.Trajectory });
-
-        return parsed
-            .apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 }, { ref: StateElements.Model });
-    }
-
-    private applyState(tree: StateBuilder) {
-        return PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
-    }
-
-    async load({ url, format = 'cif', assemblyId = 'deposited' }: LoadParams) {
-        if (!url) return
-
-        const state = this.plugin.state.dataState;
-        await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state, ref: state.tree.root.ref });
-
-        const isBinary = format === 'bcif'
-        const modelTree = this.model(this.download(state.build().toRoot(), url, isBinary), format);
-        await this.applyState(modelTree);
-
-        await (this.plugin.customState as StructureViewerState).structureControlsHelper.setAssembly(assemblyId)
-
-        Scheduler.setImmediate(() => PluginCommands.Camera.Reset.dispatch(this.plugin, { }));
-    }
-
     async loadPdbId(pdbId: string, assemblyId = 'deposited') {
-        return this.load({
+        const p = this.props.modelUrlProvider(pdbId)
+        return (this.plugin.customState as StructureViewerState).modelLoader.load({
+            fileOrUrl: p.url,
+            format: p.format,
             assemblyId,
-            ...this.props.modelUrlProvider(pdbId),
         })
     }
-
-    experimentalData = {
-        init: async () => {
-            const model = this.state.select(StateElements.Model)[0].obj;
-            const asm = this.state.select(StateElements.Assembly)[0].obj;
-            if (!model || !asm) return
-
-            const m = model.data as Model
-            const d = m.sourceData.data
-            const hasXrayMap = d.pdbx_database_status.status_code_sf.value(0) === 'REL'
-            let hasEmMap = false
-            for (let i = 0, il = d.pdbx_database_related._rowCount; i < il; ++i) {
-                if (d.pdbx_database_related.db_name.value(i).toUpperCase() === 'EMDB') {
-                    hasEmMap = true
-                    break
-                }
-            }
-
-            if (hasXrayMap || hasEmMap) {
-                const params = PD.getDefaultValues(InitVolumeStreaming.definition.params!(asm, this.plugin));
-                params.defaultView = 'selection-box';
-                params.options.behaviorRef = StateElements.VolumeStreaming;
-                params.options.serverUrl = this.props.volumeServerUrl
-                await this.plugin.runTask(this.state.applyAction(InitVolumeStreaming, params, StateElements.Assembly));
-            }
-        },
-        remove: async () => {
-            const r = this.state.select(StateSelection.Generators.ofTransformer(CreateVolumeStreamingInfo))[0];
-            if (!r) return;
-            await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state: this.state, ref: r.transform.ref });
-        }
-    }
 }

+ 21 - 7
src/structure-viewer/helpers.ts → src/structure-viewer/types.ts

@@ -4,13 +4,23 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { StructureControlsHelper } from './ui/structure';
-import { StructureViewer } from '.';
+import { StructureView } from './helpers/structure';
+import { ModelLoader } from './helpers/model';
+import { VolumeData } from './helpers/volume';
 
-export type SupportedFormats = 'cif' | 'bcif' | 'pdb'
+export interface StructureViewerProps {
+    volumeServerUrl: string,
+    modelUrlProvider: (pdbId: string) => {
+        url: string,
+        format: SupportedFormats
+    },
+    showOpenFileControls: boolean,
+}
+
+export type SupportedFormats = 'cif' | 'bcif'
 export interface LoadParams {
-    /** URL pointing to a structure file  */
-    url: string,
+    /** A File object or URL representing a structure file  */
+    fileOrUrl: File | string,
     /** A supported file format extension string */
     format?: SupportedFormats,
     /**
@@ -22,6 +32,8 @@ export interface LoadParams {
      * - 'crystal-contacts' for the symmetry mates of an X-ray structure
      */
     assemblyId?: string,
+    // TODO The modelId to show initially
+    // modelId?: string
 }
 
 export enum StateElements {
@@ -42,6 +54,8 @@ export enum AssemblyNames {
 }
 
 export interface StructureViewerState {
-    structureControlsHelper: StructureControlsHelper
-    experimentalData: StructureViewer['experimentalData']
+    props: StructureViewerProps
+    modelLoader: ModelLoader
+    structureView: StructureView
+    volumeData: VolumeData
 }

+ 4 - 1
src/structure-viewer/ui/controls.tsx

@@ -9,7 +9,7 @@ import { PluginUIComponent } from 'molstar/lib/mol-plugin/ui/base';
 import { TransformUpdaterControl } from 'molstar/lib/mol-plugin/ui/state/update-transform';
 import { StructureSelectionControls } from 'molstar/lib/mol-plugin/ui/structure/selection';
 import { StructureRepresentationControls } from 'molstar/lib/mol-plugin/ui/structure/representation';
-import { StateElements } from '../helpers';
+import { StateElements, StructureViewerState } from '../types';
 import { Viewport, ViewportControls } from 'molstar/lib/mol-plugin/ui/viewport';
 import { BackgroundTaskProgress } from 'molstar/lib/mol-plugin/ui/task';
 import { ImageControls } from 'molstar/lib/mol-plugin/ui/image';
@@ -19,6 +19,7 @@ import { Toasts } from 'molstar/lib/mol-plugin/ui/toast';
 import { GeneralSettings } from './general';
 import { StructureControls } from './structure';
 import { HelpContent } from './help';
+import { OpenFile } from './open';
 
 export class ControlsWrapper extends PluginUIComponent {
     componentDidMount() {
@@ -27,7 +28,9 @@ export class ControlsWrapper extends PluginUIComponent {
     }
 
     render() {
+        const { showOpenFileControls } = (this.plugin.customState as StructureViewerState).props
         return <div className='msp-scrollable-container msp-right-controls' style={{ paddingTop: '0px' }}>
+            {showOpenFileControls && <OpenFile initiallyCollapsed={false} />}
             <GeneralSettings initiallyCollapsed={true} />
             <StructureControls  />
             <StructureSelectionControls header='Manage Selection' initiallyCollapsed={true} />

+ 36 - 0
src/structure-viewer/ui/open.tsx

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import * as React from 'react';
+import { ParamDefinition as PD } from 'molstar/lib/mol-util/param-definition';
+import { StateAction, StateTransform } from 'molstar/lib/mol-state';
+import { PluginStateObject } from 'molstar/lib/mol-plugin/state/objects';
+import { PluginContext } from 'molstar/lib/mol-plugin/context';
+import { Task } from 'molstar/lib/mol-task';
+import { ApplyActionControl } from 'molstar/lib/mol-plugin/ui/state/apply-action';
+import { PluginUIComponent } from 'molstar/lib/mol-plugin/ui/base';
+import { StructureViewerState } from '../types';
+
+const OpenFileAction = StateAction.build({
+    display: { name: 'Open mmCIF File', description: 'Load a file and create its default visuals' },
+    from: PluginStateObject.Root,
+    params: (a, ctx: PluginContext) => {
+        return {
+            file: PD.File({ accept: '.cif, .mcif, .mmcif, .bcif' })
+        }
+    }
+})(({ params, state }, ctx: PluginContext) => Task.create('Open File', async taskCtx => {
+    await (ctx.customState as StructureViewerState).modelLoader.load({
+        fileOrUrl: params.file,
+        format: 'cif',
+    })
+}));
+
+export class OpenFile extends PluginUIComponent<{ initiallyCollapsed?: boolean }> {
+    render() {
+        return <ApplyActionControl plugin={this.plugin} key={`${OpenFileAction.id}`} state={this.plugin.state.dataState} action={OpenFileAction} nodeRef={StateTransform.RootRef} initiallyCollapsed={this.props.initiallyCollapsed} />
+    }
+}

+ 7 - 110
src/structure-viewer/ui/structure.tsx

@@ -6,128 +6,25 @@
 
 import * as React from 'react';
 import { CollapsableControls, CollapsableState } from 'molstar/lib/mol-plugin/ui/base';
-import { StateElements, AssemblyNames, StructureViewerState } from '../helpers';
+import { StateElements, AssemblyNames, StructureViewerState } from '../types';
 import { ParameterControls } from 'molstar/lib/mol-plugin/ui/controls/parameters';
 import { ParamDefinition as PD } from 'molstar/lib/mol-util/param-definition';
-import { PluginCommands } from 'molstar/lib/mol-plugin/command';
-import { StateObject, StateBuilder, StateTree, StateSelection, State } from 'molstar/lib/mol-state';
+import { StateObject, StateTree, StateSelection } from 'molstar/lib/mol-state';
 import { PluginStateObject as PSO } from 'molstar/lib/mol-plugin/state/objects';
 import { StateTransforms } from 'molstar/lib/mol-plugin/state/transforms';
-import { Vec3 } from 'molstar/lib/mol-math/linear-algebra';
 import { Model } from 'molstar/lib/mol-model/structure';
-import { PluginContext } from 'molstar/lib/mol-plugin/context';
-import { Scheduler } from 'molstar/lib/mol-task';
 
 interface StructureControlsState extends CollapsableState {
     trajectoryRef: string
 }
 
-export class StructureControlsHelper {
-    applyState(tree: StateBuilder) {
-        return PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
-    }
-
-    get experimentalData () {
-        return (this.plugin.customState as StructureViewerState).experimentalData
-    }
-
-    async preset() {
-        await this.plugin.helpers.structureRepresentation.preset()
-        Scheduler.setImmediate(() => PluginCommands.Camera.Reset.dispatch(this.plugin, { }))
-    }
-
-    private ensureModelUnitcell(tree: StateBuilder.Root, state: State) {
-        if (!state.tree.transforms.has(StateElements.ModelUnitcell)) {
-            tree.to(StateElements.Model).apply(
-                StateTransforms.Representation.ModelUnitcell3D,
-                undefined, { ref: StateElements.ModelUnitcell }
-            )
-        }
-    }
-
-    async setAssembly(id: string) {
-        const state = this.plugin.state.dataState;
-        const tree = state.build();
-        if (id === AssemblyNames.Unitcell) {
-            const props = { ijkMin: Vec3.create(0, 0, 0), ijkMax: Vec3.create(0, 0, 0) }
-            tree.delete(StateElements.Assembly)
-                .to(StateElements.Model).apply(
-                    StateTransforms.Model.StructureSymmetryFromModel,
-                    props, { ref: StateElements.Assembly, tags: [ AssemblyNames.Unitcell ] }
-                )
-            this.ensureModelUnitcell(tree, state)
-        } else if (id === AssemblyNames.Supercell) {
-            const props = { ijkMin: Vec3.create(-1, -1, -1), ijkMax: Vec3.create(1, 1, 1) }
-            tree.delete(StateElements.Assembly)
-                .to(StateElements.Model).apply(
-                    StateTransforms.Model.StructureSymmetryFromModel,
-                    props, { ref: StateElements.Assembly, tags: [ AssemblyNames.Supercell ] }
-                )
-            this.ensureModelUnitcell(tree, state)
-        } else if (id === AssemblyNames.CrystalContacts) {
-            const props = { radius: 5 }
-            tree.delete(StateElements.ModelUnitcell)
-            tree.delete(StateElements.Assembly)
-                .to(StateElements.Model).apply(
-                    StateTransforms.Model.StructureSymmetryMatesFromModel,
-                    props, { ref: StateElements.Assembly, tags: [ AssemblyNames.CrystalContacts ] }
-                )
-        } else {
-            tree.delete(StateElements.ModelUnitcell)
-            tree.delete(StateElements.Assembly)
-                .to(StateElements.Model).apply(
-                    StateTransforms.Model.StructureAssemblyFromModel,
-                    { id }, { ref: StateElements.Assembly }
-                )
-        }
-        await this.applyState(tree)
-        await this.preset()
-        await this.experimentalData.init()
-    }
-
-    async setModel(modelIndex: number) {
-        const state = this.plugin.state.dataState;
-        const tree = state.build();
-        if (modelIndex === -1) {
-            tree.delete(StateElements.Model)
-                .to(StateElements.Trajectory).apply(
-                    StateTransforms.Model.StructureFromTrajectory,
-                    {}, { ref: StateElements.Assembly }
-                )
-        } else {
-            if (state.tree.transforms.has(StateElements.Model)) {
-                tree.to(StateElements.Model).update(
-                    StateTransforms.Model.ModelFromTrajectory,
-                    props => ({ ...props, modelIndex })
-                )
-            } else {
-                tree.delete(StateElements.Assembly)
-                    .to(StateElements.Trajectory).apply(
-                        StateTransforms.Model.ModelFromTrajectory,
-                        { modelIndex }, { ref: StateElements.Model }
-                    )
-                    .apply(
-                        StateTransforms.Model.StructureAssemblyFromModel,
-                        { id: AssemblyNames.Deposited }, { ref: StateElements.Assembly }
-                    )
-            }
-        }
-        await this.applyState(tree)
-        await this.preset()
-    }
-
-    constructor(private plugin: PluginContext) {
-
-    }
-}
-
 export class StructureControls<P, S extends StructureControlsState> extends CollapsableControls<P, S> {
     constructor(props: P, context?: any) {
         super(props, context);
     }
 
-    get structureControlsHelper () {
-        return (this.plugin.customState as StructureViewerState).structureControlsHelper
+    get structureView () {
+        return (this.plugin.customState as StructureViewerState).structureView
     }
 
     async setColorTheme(theme: { [k: string]: string }) {
@@ -148,15 +45,15 @@ export class StructureControls<P, S extends StructureControlsState> extends Coll
                 )
             }
         })
-        await this.structureControlsHelper.applyState(tree)
+        await this.structureView.applyState(tree)
     }
 
     onChange = async (p: { param: PD.Base<any>, name: string, value: any }) => {
         // console.log('onChange', p.name, p.value)
         if (p.name === 'assembly') {
-            this.structureControlsHelper.setAssembly(p.value)
+            this.structureView.setAssembly(p.value)
         } else if (p.name === 'model') {
-            this.structureControlsHelper.setModel(p.value)
+            this.structureView.setModel(p.value)
         } else if (p.name === 'colorThemes') {
             this.setColorTheme(p.value)
         }