Skip to content
Snippets Groups Projects
Commit 72a156ed authored by David Sehnal's avatar David Sehnal
Browse files

mol-plugin: switch between data and behavior trees

parent 458eae3b
No related branches found
No related tags found
No related merge requests found
......@@ -17,7 +17,7 @@ export function registerDefault(ctx: PluginContext) {
export function Reset(ctx: PluginContext) {
PluginCommands.Camera.Reset.subscribe(ctx, () => {
const sel = ctx.state.data.select(q => q.root.subtree().ofType(SO.Molecule.Structure));
const sel = ctx.state.dataState.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;
......
......@@ -12,14 +12,15 @@ export function registerDefault(ctx: PluginContext) {
}
export function SyncRepresentationToCanvas(ctx: PluginContext) {
ctx.events.state.data.object.created.subscribe(e => {
const events = ctx.state.dataState.events;
events.object.created.subscribe(e => {
if (!SO.isRepresentation3D(e.obj)) return;
ctx.canvas3d.add(e.obj.data);
ctx.canvas3d.requestDraw(true);
// TODO: update visiblity
});
ctx.events.state.data.object.updated.subscribe(e => {
events.object.updated.subscribe(e => {
if (e.oldObj && SO.isRepresentation3D(e.oldObj)) {
ctx.canvas3d.remove(e.oldObj.data);
ctx.canvas3d.requestDraw(true);
......@@ -32,7 +33,7 @@ export function SyncRepresentationToCanvas(ctx: PluginContext) {
ctx.canvas3d.add(e.obj.data);
ctx.canvas3d.requestDraw(true);
});
ctx.events.state.data.object.removed.subscribe(e => {
events.object.removed.subscribe(e => {
const oo = e.obj;
if (!SO.isRepresentation3D(oo)) return;
ctx.canvas3d.remove(oo.data);
......@@ -42,7 +43,7 @@ export function SyncRepresentationToCanvas(ctx: PluginContext) {
}
export function UpdateRepresentationVisibility(ctx: PluginContext) {
ctx.events.state.data.object.cellState.subscribe(e => {
ctx.state.dataState.events.object.cellState.subscribe(e => {
const cell = e.state.cells.get(e.ref)!;
if (!SO.isRepresentation3D(cell.obj)) return;
......
......@@ -30,8 +30,16 @@ export class PluginContext {
readonly events = {
state: {
data: this.state.data.events,
behavior: this.state.behavior.events,
object: {
cellState: merge(this.state.dataState.events.object.cellState, this.state.behaviorState.events.object.cellState),
cellCreated: merge(this.state.dataState.events.object.cellCreated, this.state.behaviorState.events.object.cellCreated),
created: merge(this.state.dataState.events.object.created, this.state.behaviorState.events.object.created),
removed: merge(this.state.dataState.events.object.removed, this.state.behaviorState.events.object.removed),
updated: merge(this.state.dataState.events.object.updated, this.state.behaviorState.events.object.updated)
},
// data: this.state.dataState.events,
// behavior: this.state.behaviorState.events,
cameraSnapshots: this.state.cameraSnapshots.events,
snapshots: this.state.snapshots.events,
},
......@@ -40,10 +48,10 @@ export class PluginContext {
};
readonly behaviors = {
state: {
data: this.state.data.behaviors,
behavior: this.state.behavior.behaviors
},
// state: {
// data: this.state.dataState.behaviors,
// behavior: this.state.behaviorState.behaviors
// },
canvas: {
highlightLoci: this.ev.behavior<{ loci: Loci, repr?: Representation.Any }>({ loci: EmptyLoci }),
selectLoci: this.ev.behavior<{ loci: Loci, repr?: Representation.Any }>({ loci: EmptyLoci }),
......@@ -98,20 +106,20 @@ export class PluginContext {
BuiltInPluginBehaviors.Representation.registerDefault(this);
BuiltInPluginBehaviors.Camera.registerDefault(this);
merge(this.state.data.events.log, this.state.behavior.events.log).subscribe(e => this.events.log.next(e));
merge(this.state.dataState.events.log, this.state.behaviorState.events.log).subscribe(e => this.events.log.next(e));
}
async _test_initBehaviors() {
const tree = this.state.behavior.tree.build()
const tree = this.state.behaviorState.tree.build()
.toRoot().apply(PluginBehaviors.Representation.HighlightLoci, { ref: PluginBehaviors.Representation.HighlightLoci.id })
.toRoot().apply(PluginBehaviors.Representation.SelectLoci, { ref: PluginBehaviors.Representation.SelectLoci.id })
.getTree();
await this.runTask(this.state.behavior.update(tree));
await this.runTask(this.state.behaviorState.update(tree));
}
_test_initDataActions() {
this.state.data.actions
this.state.dataState.actions
.add(CreateStructureFromPDBe)
.add(StateTransforms.Data.Download)
.add(StateTransforms.Data.ParseCif)
......@@ -132,18 +140,17 @@ export class PluginContext {
}
private initEvents() {
merge(this.events.state.data.object.created, this.events.state.behavior.object.created).subscribe(o => {
this.events.state.object.created.subscribe(o => {
if (!SO.isBehavior(o.obj)) return;
console.log('registering behavior', o.obj.label);
o.obj.data.register();
});
merge(this.events.state.data.object.removed, this.events.state.behavior.object.removed).subscribe(o => {
this.events.state.object.removed.subscribe(o => {
if (!SO.isBehavior(o.obj)) return;
o.obj.data.unregister();
});
merge(this.events.state.data.object.updated, this.events.state.behavior.object.updated).subscribe(o => {
this.events.state.object.updated.subscribe(o => {
if (o.action === 'recreate') {
if (o.oldObj && SO.isBehavior(o.oldObj)) o.oldObj.data.unregister();
if (o.obj && SO.isBehavior(o.obj)) o.obj.data.register();
......
......@@ -10,20 +10,37 @@ import { Camera } from 'mol-canvas3d/camera';
import { PluginBehavior } from './behavior';
import { CameraSnapshotManager } from './state/camera';
import { PluginStateSnapshotManager } from './state/snapshots';
import { RxEventHelper } from 'mol-util/rx-event-helper';
export { PluginState }
class PluginState {
readonly data: State;
readonly behavior: State;
private ev = RxEventHelper.create();
readonly dataState: State;
readonly behaviorState: State;
readonly cameraSnapshots = new CameraSnapshotManager();
readonly snapshots = new PluginStateSnapshotManager();
readonly behavior = {
kind: this.ev.behavior<PluginState.Kind>('data'),
currentObject: this.ev.behavior<State.ObjectEvent>({} as any)
}
setKind(kind: PluginState.Kind) {
const current = this.behavior.kind.value;
if (kind !== current) {
this.behavior.kind.next(kind);
this.behavior.currentObject.next(kind === 'data'
? this.dataState.behaviors.currentObject.value
: this.behaviorState.behaviors.currentObject.value)
}
}
getSnapshot(): PluginState.Snapshot {
return {
data: this.data.getSnapshot(),
behaviour: this.behavior.getSnapshot(),
data: this.dataState.getSnapshot(),
behaviours: this.behaviorState.getSnapshot(),
cameraSnapshots: this.cameraSnapshots.getStateSnapshot(),
canvas3d: {
camera: this.plugin.canvas3d.camera.getSnapshot()
......@@ -32,29 +49,41 @@ class PluginState {
}
async setSnapshot(snapshot: PluginState.Snapshot) {
await this.plugin.runTask(this.behavior.setSnapshot(snapshot.behaviour));
await this.plugin.runTask(this.data.setSnapshot(snapshot.data));
await this.plugin.runTask(this.behaviorState.setSnapshot(snapshot.behaviours));
await this.plugin.runTask(this.dataState.setSnapshot(snapshot.data));
this.cameraSnapshots.setStateSnapshot(snapshot.cameraSnapshots);
this.plugin.canvas3d.camera.setState(snapshot.canvas3d.camera);
this.plugin.canvas3d.requestDraw(true);
}
dispose() {
this.data.dispose();
this.behavior.dispose();
this.ev.dispose();
this.dataState.dispose();
this.behaviorState.dispose();
this.cameraSnapshots.dispose();
}
constructor(private plugin: import('./context').PluginContext) {
this.data = State.create(new SO.Root({ }), { globalContext: plugin });
this.behavior = State.create(new PluginBehavior.Root({ }), { globalContext: plugin });
this.dataState = State.create(new SO.Root({ }), { globalContext: plugin });
this.behaviorState = State.create(new PluginBehavior.Root({ }), { globalContext: plugin });
this.dataState.behaviors.currentObject.subscribe(o => {
if (this.behavior.kind.value === 'data') this.behavior.currentObject.next(o);
});
this.behaviorState.behaviors.currentObject.subscribe(o => {
if (this.behavior.kind.value === 'behavior') this.behavior.currentObject.next(o);
});
this.behavior.currentObject.next(this.dataState.behaviors.currentObject.value);
}
}
namespace PluginState {
export type Kind = 'data' | 'behavior'
export interface Snapshot {
data: State.Snapshot,
behaviour: State.Snapshot,
behaviours: State.Snapshot,
cameraSnapshots: CameraSnapshotManager.StateSnapshot,
canvas3d: {
camera: Camera.Snapshot
......
......@@ -118,17 +118,23 @@ class ActionContol extends PluginComponent<ActionContol.Props, { params: any, in
state = this.defaultState()
nothingToUpdate() {
return <div>Nothing to update</div>;
}
render() {
console.log('render', this.props.nodeRef, this.action.id);
const cell = this.cell;
if (cell.status !== 'ok' || (this.isUpdate && cell.transform.ref === Transform.RootRef)) return null;
if (cell.status !== 'ok' || (this.isUpdate && cell.transform.ref === Transform.RootRef)) return this.nothingToUpdate();
const paramDefs = this.getParamDefinitions();
if (this.isUpdate && Object.keys(paramDefs).length === 0) return this.nothingToUpdate();
const action = this.action;
return <div>
<div style={{ borderBottom: '1px solid #999', marginBottom: '5px' }}><h3>{(action.definition.display && action.definition.display.name) || action.id}</h3></div>
<ParameterControls params={this.getParamDefinitions()} values={this.state.params} changes={this.changes} onEnter={this.onEnter} isEnabled={!this.state.busy} />
<ParameterControls params={paramDefs} values={this.state.params} changes={this.changes} onEnter={this.onEnter} isEnabled={!this.state.busy} />
<div style={{ textAlign: 'right' }}>
<span style={{ color: 'red' }}>{this.state.error}</span>
......
......@@ -22,15 +22,15 @@ export class TrajectoryControls extends PluginComponent {
return <div>
<b>Trajectory: </b>
<button onClick={() => PluginCommands.State.ApplyAction.dispatch(this.plugin, {
state: this.plugin.state.data,
state: this.plugin.state.dataState,
action: UpdateTrajectory.create({ action: 'advance', by: -1 })
})}>&lt;&lt;</button>
<button onClick={() => PluginCommands.State.ApplyAction.dispatch(this.plugin, {
state: this.plugin.state.data,
state: this.plugin.state.dataState,
action: UpdateTrajectory.create({ action: 'reset' })
})}>Reset</button>
<button onClick={() => PluginCommands.State.ApplyAction.dispatch(this.plugin, {
state: this.plugin.state.data,
state: this.plugin.state.dataState,
action: UpdateTrajectory.create({ action: 'advance', by: +1 })
})}>&gt;&gt;</button><br />
</div>
......
......@@ -10,7 +10,6 @@ import { StateTree } from './state-tree';
import { Viewport, ViewportControls } from './viewport';
import { Controls, TrajectoryControls } from './controls';
import { PluginComponent, PluginReactContext } from './base';
import { merge } from 'rxjs';
import { CameraSnapshots } from './camera';
import { StateSnapshots } from './state';
import { List } from 'immutable';
......@@ -18,15 +17,14 @@ import { LogEntry } from 'mol-util/log-entry';
import { formatTime } from 'mol-util';
import { BackgroundTaskProgress } from './task';
import { ActionContol } from './action';
import { PluginState } from 'mol-plugin/state';
export class Plugin extends React.Component<{ plugin: PluginContext }, {}> {
render() {
return <PluginReactContext.Provider value={this.props.plugin}>
<div style={{ position: 'absolute', width: '100%', height: '100%', fontFamily: 'monospace' }}>
<div style={{ position: 'absolute', width: '350px', height: '100%', overflowY: 'scroll', padding: '10px' }}>
<StateTree state={this.props.plugin.state.data} />
<h3>Behaviors</h3>
<StateTree state={this.props.plugin.state.behavior} />
<State />
</div>
<div style={{ position: 'absolute', left: '350px', right: '300px', top: '0', bottom: '100px' }}>
<Viewport />
......@@ -55,6 +53,26 @@ export class Plugin extends React.Component<{ plugin: PluginContext }, {}> {
}
}
export class State extends PluginComponent {
componentDidMount() {
this.subscribe(this.plugin.state.behavior.kind, () => this.forceUpdate());
}
set(kind: PluginState.Kind) {
// TODO: do command for this?
this.plugin.state.setKind(kind);
}
render() {
const kind = this.plugin.state.behavior.kind.value;
return <>
<button onClick={() => this.set('data')} style={{ fontWeight: kind === 'data' ? 'bold' : 'normal'}}>Data</button>
<button onClick={() => this.set('behavior')} style={{ fontWeight: kind === 'behavior' ? 'bold' : 'normal'}}>Behavior</button>
<StateTree state={kind === 'data' ? this.plugin.state.dataState : this.plugin.state.behaviorState} />
</>
}
}
export class Log extends PluginComponent<{}, { entries: List<LogEntry> }> {
private wrapper = React.createRef<HTMLDivElement>();
......@@ -85,33 +103,33 @@ export class Log extends PluginComponent<{}, { entries: List<LogEntry> }> {
}
export class CurrentObject extends PluginComponent {
get current() {
return this.plugin.state.behavior.currentObject.value;
}
componentDidMount() {
// let current: State.ObjectEvent | undefined = void 0;
this.subscribe(merge(this.plugin.behaviors.state.data.currentObject, this.plugin.behaviors.state.behavior.currentObject), o => {
// current = o;
this.forceUpdate()
this.subscribe(this.plugin.state.behavior.currentObject, o => {
this.forceUpdate();
});
this.subscribe(this.plugin.events.state.data.object.updated, ({ ref, state }) => {
console.log('curr event', +new Date);
const current = this.plugin.behaviors.state.data.currentObject.value;
this.subscribe(this.plugin.events.state.object.updated, ({ ref, state }) => {
const current = this.current;
if (current.ref !== ref || current.state !== state) return;
console.log('curr event pass', +new Date);
this.forceUpdate();
});
}
render() {
console.log('curr', +new Date);
const current = this.plugin.behaviors.state.data.currentObject.value;
const current = this.current;
const ref = current.ref;
// const n = this.props.plugin.state.data.tree.nodes.get(ref)!;
const obj = this.plugin.state.data.cells.get(ref)!;
const obj = current.state.cells.get(ref)!;
const type = obj && obj.obj ? obj.obj.type : void 0;
console.log(obj);
const actions = type
? current.state.actions.fromType(type)
: []
......
......@@ -9,7 +9,6 @@ import { PluginStateObject } from 'mol-plugin/state/objects';
import { State } from 'mol-state'
import { PluginCommands } from 'mol-plugin/command';
import { PluginComponent } from './base';
import { merge } from 'rxjs';
export class StateTree extends PluginComponent<{ state: State }, { }> {
componentDidMount() {
......@@ -28,7 +27,7 @@ export class StateTree extends PluginComponent<{ state: State }, { }> {
export class StateTreeNode extends PluginComponent<{ nodeRef: string, state: State }, { }> {
componentDidMount() {
this.subscribe(merge(this.plugin.events.state.data.object.cellState, this.plugin.events.state.behavior.object.cellState), o => {
this.subscribe(this.plugin.events.state.object.cellState, o => {
if (o.ref === this.props.nodeRef && o.state === this.props.state) this.forceUpdate();
});
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment