From 2d10e5d648282b405046abde8e20f10f0edada03 Mon Sep 17 00:00:00 2001
From: David Sehnal <david.sehnal@gmail.com>
Date: Mon, 12 Nov 2018 19:35:39 +0100
Subject: [PATCH] mol-plugin: wip

---
 src/mol-plugin/behavior.ts               |  2 +-
 src/mol-plugin/behavior/static/camera.ts | 17 ++++++++++++++
 src/mol-plugin/command.ts                |  5 ++++-
 src/mol-plugin/command/camera.ts         |  9 ++++++++
 src/mol-plugin/context.ts                | 13 -----------
 src/mol-plugin/state/camera.ts           |  0
 src/mol-plugin/ui/base.tsx               |  3 ++-
 src/mol-plugin/ui/controls.tsx           | 28 +++++++++++-------------
 src/mol-plugin/ui/plugin.tsx             | 11 +++++-----
 src/mol-plugin/ui/state-tree.tsx         | 12 +++++-----
 src/mol-plugin/ui/viewport.tsx           | 21 ++++++++++++++----
 src/mol-state/state/selection.ts         |  6 ++---
 12 files changed, 78 insertions(+), 49 deletions(-)
 create mode 100644 src/mol-plugin/command/camera.ts
 create mode 100644 src/mol-plugin/state/camera.ts

diff --git a/src/mol-plugin/behavior.ts b/src/mol-plugin/behavior.ts
index 5b14cd765..307b3f134 100644
--- a/src/mol-plugin/behavior.ts
+++ b/src/mol-plugin/behavior.ts
@@ -8,7 +8,7 @@ export * from './behavior/behavior'
 
 import * as StaticState from './behavior/static/state'
 import * as StaticRepresentation from './behavior/static/representation'
-import * as StaticCamera from './behavior/static/representation'
+import * as StaticCamera from './behavior/static/camera'
 
 import * as DynamicRepresentation from './behavior/dynamic/representation'
 
diff --git a/src/mol-plugin/behavior/static/camera.ts b/src/mol-plugin/behavior/static/camera.ts
index 808b06a21..1199b2fe4 100644
--- a/src/mol-plugin/behavior/static/camera.ts
+++ b/src/mol-plugin/behavior/static/camera.ts
@@ -5,6 +5,23 @@
  */
 
 import { PluginContext } from 'mol-plugin/context';
+import { PluginCommands } from 'mol-plugin/command';
+import { PluginStateObject as SO } from '../../state/objects';
 
 export function registerDefault(ctx: PluginContext) {
+    Reset(ctx);
 }
+
+export function Reset(ctx: PluginContext) {
+    PluginCommands.Camera.Reset.subscribe(ctx, () => {
+        const sel = ctx.state.data.select(q => q.root.subtree().ofType(SO.Molecule.Structure));
+        if (!sel.length) return;
+
+        const center = (sel[0].obj! as SO.Molecule.Structure).data.boundary.sphere.center;
+        ctx.canvas3d.camera.setState({ target: center });
+        ctx.canvas3d.requestDraw(true);
+
+        // TODO
+        // ctx.canvas3d.resetCamera();
+    })
+}
\ No newline at end of file
diff --git a/src/mol-plugin/command.ts b/src/mol-plugin/command.ts
index 3d60d41fd..ec948ef85 100644
--- a/src/mol-plugin/command.ts
+++ b/src/mol-plugin/command.ts
@@ -5,8 +5,11 @@
  */
 
 import * as State from './command/state';
+import * as Camera from './command/camera';
 
 export * from './command/command';
+
 export const PluginCommands = {
-    State
+    State,
+    Camera
 }
\ No newline at end of file
diff --git a/src/mol-plugin/command/camera.ts b/src/mol-plugin/command/camera.ts
new file mode 100644
index 000000000..22a07d503
--- /dev/null
+++ b/src/mol-plugin/command/camera.ts
@@ -0,0 +1,9 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { PluginCommand } from './command';
+
+export const Reset = PluginCommand<{}>({ isImmediate: true });
\ No newline at end of file
diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts
index c5a4f2334..a36603a5e 100644
--- a/src/mol-plugin/context.ts
+++ b/src/mol-plugin/context.ts
@@ -116,10 +116,6 @@ export class PluginContext {
         return PluginCommands.State.Update.dispatch(this, { state, tree });
     }
 
-    _test_createState(id: string) {
-        this.runTask(this.state.data.apply(CreateStructureFromPDBe, { id }));
-    }
-
     private initEvents() {
         merge(this.events.state.data.object.created, this.events.state.behavior.object.created).subscribe(o => {
             if (!SO.isBehavior(o.obj)) return;
@@ -140,15 +136,6 @@ export class PluginContext {
         });
     }
 
-    _test_centerView() {
-        const sel = this.state.data.select(q => q.root.subtree().ofType(SO.Molecule.Structure.type));
-        if (!sel.length) return;
-
-        const center = (sel[0].obj! as SO.Molecule.Structure).data.boundary.sphere.center;
-        this.canvas3d.camera.setState({ target: center });
-        this.canvas3d.requestDraw(true);
-    }
-
     constructor() {
         this.initEvents();
         this.initBuiltInBehavior();
diff --git a/src/mol-plugin/state/camera.ts b/src/mol-plugin/state/camera.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/mol-plugin/ui/base.tsx b/src/mol-plugin/ui/base.tsx
index e273690ff..2ea5b354e 100644
--- a/src/mol-plugin/ui/base.tsx
+++ b/src/mol-plugin/ui/base.tsx
@@ -12,7 +12,7 @@ export const PluginReactContext = React.createContext(void 0 as any as PluginCon
 
 export abstract class PluginComponent<P = {}, S = {}, SS = {}> extends React.Component<P, S, SS> {
     static contextType = PluginReactContext;
-    readonly context: PluginContext;
+    readonly plugin: PluginContext;
 
     private subs: Subscription[] | undefined = void 0;
 
@@ -30,6 +30,7 @@ export abstract class PluginComponent<P = {}, S = {}, SS = {}> extends React.Com
 
     constructor(props: P, context?: any) {
         super(props, context);
+        this.plugin = context;
         if (this.init) this.init();
     }
 }
\ No newline at end of file
diff --git a/src/mol-plugin/ui/controls.tsx b/src/mol-plugin/ui/controls.tsx
index e6154488e..ff1789b4b 100644
--- a/src/mol-plugin/ui/controls.tsx
+++ b/src/mol-plugin/ui/controls.tsx
@@ -17,18 +17,16 @@ export class Controls extends PluginComponent<{ }, { }> {
 
     private _snap: any = void 0;
     private getSnapshot = () => {
-        this._snap = this.context.state.getSnapshot();
+        this._snap = this.plugin.state.getSnapshot();
         console.log(btoa(JSON.stringify(this._snap)));
     }
     private setSnapshot = () => {
         if (!this._snap) return;
-        this.context.state.setSnapshot(this._snap);
+        this.plugin.state.setSnapshot(this._snap);
     }
 
     render() {
         return <div>
-            <button onClick={() => this.context._test_centerView()}>Center View</button><br />
-            <hr />
             <button onClick={this.getSnapshot}>Get Snapshot</button>
             <button onClick={this.setSnapshot}>Set Snapshot</button>
         </div>;
@@ -40,16 +38,16 @@ export class _test_TrajectoryControls extends PluginComponent {
     render() {
         return <div>
             <b>Trajectory: </b>
-            <button onClick={() => PluginCommands.State.ApplyAction.dispatch(this.context, {
-                state: this.context.state.data,
+            <button onClick={() => PluginCommands.State.ApplyAction.dispatch(this.plugin, {
+                state: this.plugin.state.data,
                 action: UpdateTrajectory.create({ action: 'advance', by: -1 })
             })}>&lt;&lt;</button>
-            <button onClick={() => PluginCommands.State.ApplyAction.dispatch(this.context, {
-                state: this.context.state.data,
+            <button onClick={() => PluginCommands.State.ApplyAction.dispatch(this.plugin, {
+                state: this.plugin.state.data,
                 action: UpdateTrajectory.create({ action: 'reset' })
             })}>Reset</button>
-            <button onClick={() => PluginCommands.State.ApplyAction.dispatch(this.context, {
-                state: this.context.state.data,
+            <button onClick={() => PluginCommands.State.ApplyAction.dispatch(this.plugin, {
+                state: this.plugin.state.data,
                 action: UpdateTrajectory.create({ action: 'advance', by: +1 })
             })}>&gt;&gt;</button><br />
         </div>
@@ -67,7 +65,7 @@ export class _test_ApplyAction extends PluginComponent<{ nodeRef: Transform.Ref,
         if (!p || !p.default) return {};
         const obj = this.getObj();
         if (!obj.obj) return {};
-        return p.default(obj.obj, this.context);
+        return p.default(obj.obj, this.plugin);
     }
 
     private getParamDef() {
@@ -75,12 +73,12 @@ export class _test_ApplyAction extends PluginComponent<{ nodeRef: Transform.Ref,
         if (!p || !p.controls) return {};
         const obj = this.getObj();
         if (!obj.obj) return {};
-        return p.controls(obj.obj, this.context);
+        return p.controls(obj.obj, this.plugin);
     }
 
     private create() {
         console.log('Apply Action', this.state.params);
-        PluginCommands.State.ApplyAction.dispatch(this.context, {
+        PluginCommands.State.ApplyAction.dispatch(this.plugin, {
             state: this.props.state,
             action: this.props.action.create(this.state.params),
             ref: this.props.nodeRef
@@ -130,12 +128,12 @@ export class _test_UpdateTransform extends PluginComponent<{ state: State, nodeR
         const src = this.getCell(cell.sourceRef);
         if (!src || !src.obj) return void 0;
 
-        return def.params.controls(src.obj, this.context);
+        return def.params.controls(src.obj, this.plugin);
     }
 
     private update() {
         console.log(this.props.nodeRef, this.state.params);
-        this.context.updateTransform(this.props.state, this.props.nodeRef, this.state.params);
+        this.plugin.updateTransform(this.props.state, this.props.nodeRef, this.state.params);
     }
 
     // componentDidMount() {
diff --git a/src/mol-plugin/ui/plugin.tsx b/src/mol-plugin/ui/plugin.tsx
index 37c41e7da..76b816793 100644
--- a/src/mol-plugin/ui/plugin.tsx
+++ b/src/mol-plugin/ui/plugin.tsx
@@ -7,7 +7,7 @@
 import * as React from 'react';
 import { PluginContext } from '../context';
 import { StateTree } from './state-tree';
-import { Viewport } from './viewport';
+import { Viewport, ViewportControls } from './viewport';
 import { Controls, _test_UpdateTransform, _test_ApplyAction, _test_TrajectoryControls } from './controls';
 import { PluginComponent, PluginReactContext } from './base';
 import { merge } from 'rxjs';
@@ -27,6 +27,7 @@ export class Plugin extends React.Component<{ plugin: PluginContext }, {}> {
                     <div style={{ position: 'absolute', left: '10px', top: '10px', height: '100%', color: 'white' }}>
                         <_test_TrajectoryControls />
                     </div>
+                    <ViewportControls />
                 </div>
                 <div style={{ position: 'absolute', width: '300px', right: '0', height: '100%', padding: '10px' }}>
                     <_test_CurrentObject />
@@ -41,23 +42,23 @@ export class Plugin extends React.Component<{ plugin: PluginContext }, {}> {
 export class _test_CurrentObject extends PluginComponent {
     componentDidMount() {
         let current: State.ObjectEvent | undefined = void 0;
-        this.subscribe(merge(this.context.behaviors.state.data.currentObject, this.context.behaviors.state.behavior.currentObject), o => {
+        this.subscribe(merge(this.plugin.behaviors.state.data.currentObject, this.plugin.behaviors.state.behavior.currentObject), o => {
             current = o;
             this.forceUpdate()
         });
 
-        this.subscribe(this.context.events.state.data.object.updated, ({ ref, state }) => {
+        this.subscribe(this.plugin.events.state.data.object.updated, ({ ref, state }) => {
             if (!current || current.ref !== ref && current.state !== state) return;
             this.forceUpdate();
         });
     }
 
     render() {
-        const current = this.context.behaviors.state.data.currentObject.value;
+        const current = this.plugin.behaviors.state.data.currentObject.value;
 
         const ref = current.ref;
         // const n = this.props.plugin.state.data.tree.nodes.get(ref)!;
-        const obj = this.context.state.data.cells.get(ref)!;
+        const obj = this.plugin.state.data.cells.get(ref)!;
 
         const type = obj && obj.obj ? obj.obj.type : void 0;
 
diff --git a/src/mol-plugin/ui/state-tree.tsx b/src/mol-plugin/ui/state-tree.tsx
index d89911d2e..7912c0a43 100644
--- a/src/mol-plugin/ui/state-tree.tsx
+++ b/src/mol-plugin/ui/state-tree.tsx
@@ -28,7 +28,7 @@ export class StateTree extends PluginComponent<{ state: State }, { }> {
 
 export class StateTreeNode extends PluginComponent<{ nodeRef: string, state: State }, { }> {
     componentDidMount() {
-        this.subscribe(merge(this.context.events.state.data.object.cellState, this.context.events.state.behavior.object.cellState), o => {
+        this.subscribe(merge(this.plugin.events.state.data.object.cellState, this.plugin.events.state.behavior.object.cellState), o => {
             if (o.ref === this.props.nodeRef && o.state === this.props.state) this.forceUpdate();
         });
     }
@@ -39,7 +39,7 @@ export class StateTreeNode extends PluginComponent<{ nodeRef: string, state: Sta
 
         const remove = <>[<a href='#' onClick={e => {
             e.preventDefault();
-            PluginCommands.State.RemoveObject.dispatch(this.context, { state: this.props.state, ref: this.props.nodeRef });
+            PluginCommands.State.RemoveObject.dispatch(this.plugin, { state: this.props.state, ref: this.props.nodeRef });
         }}>X</a>]</>
 
         let label: any;
@@ -47,13 +47,13 @@ export class StateTreeNode extends PluginComponent<{ nodeRef: string, state: Sta
             const name = (n.transformer.definition.display && n.transformer.definition.display.name) || n.transformer.definition.name;
             label = <><b>{cell.status}</b> <a href='#' onClick={e => {
                 e.preventDefault();
-                PluginCommands.State.SetCurrentObject.dispatch(this.context, { state: this.props.state, ref: this.props.nodeRef });
+                PluginCommands.State.SetCurrentObject.dispatch(this.plugin, { state: this.props.state, ref: this.props.nodeRef });
             }}>{name}</a>: <i>{cell.errorText}</i></>;
         } else {
             const obj = cell.obj as PluginStateObject.Any;
             label = <><a href='#' onClick={e => {
                 e.preventDefault();
-                PluginCommands.State.SetCurrentObject.dispatch(this.context, { state: this.props.state, ref: this.props.nodeRef });
+                PluginCommands.State.SetCurrentObject.dispatch(this.plugin, { state: this.props.state, ref: this.props.nodeRef });
             }}>{obj.label}</a> {obj.description ? <small>{obj.description}</small> : void 0}</>;
         }
 
@@ -62,14 +62,14 @@ export class StateTreeNode extends PluginComponent<{ nodeRef: string, state: Sta
         const expander = <>
             [<a href='#' onClick={e => {
                 e.preventDefault();
-                PluginCommands.State.ToggleExpanded.dispatch(this.context, { state: this.props.state, ref: this.props.nodeRef });
+                PluginCommands.State.ToggleExpanded.dispatch(this.plugin, { state: this.props.state, ref: this.props.nodeRef });
             }}>{cellState.isCollapsed ? '+' : '-'}</a>]
         </>;
 
         const visibility = <>
             [<a href='#' onClick={e => {
                 e.preventDefault();
-                PluginCommands.State.ToggleVisibility.dispatch(this.context, { state: this.props.state, ref: this.props.nodeRef });
+                PluginCommands.State.ToggleVisibility.dispatch(this.plugin, { state: this.props.state, ref: this.props.nodeRef });
             }}>{cellState.isHidden ? 'H' : 'V'}</a>]
         </>;
 
diff --git a/src/mol-plugin/ui/viewport.tsx b/src/mol-plugin/ui/viewport.tsx
index c61d2bb8b..2ce8f7bb6 100644
--- a/src/mol-plugin/ui/viewport.tsx
+++ b/src/mol-plugin/ui/viewport.tsx
@@ -9,11 +9,24 @@ import * as React from 'react';
 import { ButtonsType } from 'mol-util/input/input-observer';
 import { Canvas3dIdentifyHelper } from 'mol-plugin/util/canvas3d-identify';
 import { PluginComponent } from './base';
+import { PluginCommands } from 'mol-plugin/command';
 
 interface ViewportState {
     noWebGl: boolean
 }
 
+export class ViewportControls extends PluginComponent {
+    resetCamera = () => {
+        PluginCommands.Camera.Reset.dispatch(this.plugin, {});
+    }
+
+    render() {
+        return <div style={{ position: 'absolute', right: '10px', top: '10px', height: '100%', color: 'white' }}>
+            <button onClick={this.resetCamera}>Reset Camera</button>
+        </div>
+    }
+}
+
 export class Viewport extends PluginComponent<{ }, ViewportState> {
     private container: HTMLDivElement | null = null;
     private canvas: HTMLCanvasElement | null = null;
@@ -23,19 +36,19 @@ export class Viewport extends PluginComponent<{ }, ViewportState> {
     };
 
     private handleResize = () => {
-         this.context.canvas3d.handleResize();
+         this.plugin.canvas3d.handleResize();
     }
 
     componentDidMount() {
-        if (!this.canvas || !this.container || !this.context.initViewer(this.canvas, this.container)) {
+        if (!this.canvas || !this.container || !this.plugin.initViewer(this.canvas, this.container)) {
             this.setState({ noWebGl: true });
         }
         this.handleResize();
 
-        const canvas3d = this.context.canvas3d;
+        const canvas3d = this.plugin.canvas3d;
         this.subscribe(canvas3d.input.resize, this.handleResize);
 
-        const idHelper = new Canvas3dIdentifyHelper(this.context, 15);
+        const idHelper = new Canvas3dIdentifyHelper(this.plugin, 15);
 
         this.subscribe(canvas3d.input.move, ({x, y, inside, buttons}) => {
             if (!inside || buttons) { return; }
diff --git a/src/mol-state/state/selection.ts b/src/mol-state/state/selection.ts
index 039f80d4c..d25a82835 100644
--- a/src/mol-state/state/selection.ts
+++ b/src/mol-state/state/selection.ts
@@ -51,8 +51,8 @@ namespace StateSelection {
         withStatus(s: StateObjectCell.Status): Builder;
         subtree(): Builder;
         children(): Builder;
-        ofType(t: StateObject.Type): Builder;
-        ancestorOfType(t: StateObject.Type): Builder;
+        ofType(t: StateObject.Ctor): Builder;
+        ancestorOfType(t: StateObject.Ctor): Builder;
 
         select(state: State): CellSeq
     }
@@ -184,7 +184,7 @@ namespace StateSelection {
     }
 
     registerModifier('ofType', ofType);
-    export function ofType(b: Selector, t: StateObject.Type) { return filter(b, n => n.obj ? n.obj.type === t : false); }
+    export function ofType(b: Selector, t: StateObject.Ctor) { return filter(b, n => n.obj ? n.obj.type === t.type : false); }
 
     registerModifier('ancestorOfType', ancestorOfType);
     export function ancestorOfType(b: Selector, types: StateObject.Ctor[]) { return unique(mapEntity(b, (n, s) => findAncestorOfType(s.tree, s.cells, n.transform.ref, types))); }
-- 
GitLab