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 }) })}><<</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 }) })}>>></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