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

mol-plugin: camera snapshot manager

parent 2d10e5d6
No related branches found
No related tags found
No related merge requests found
...@@ -7,9 +7,12 @@ ...@@ -7,9 +7,12 @@
import { PluginContext } from 'mol-plugin/context'; import { PluginContext } from 'mol-plugin/context';
import { PluginCommands } from 'mol-plugin/command'; import { PluginCommands } from 'mol-plugin/command';
import { PluginStateObject as SO } from '../../state/objects'; import { PluginStateObject as SO } from '../../state/objects';
import { CameraSnapshotManager } from 'mol-plugin/state/camera';
export function registerDefault(ctx: PluginContext) { export function registerDefault(ctx: PluginContext) {
Reset(ctx); Reset(ctx);
SetSnapshot(ctx);
Snapshots(ctx);
} }
export function Reset(ctx: PluginContext) { export function Reset(ctx: PluginContext) {
...@@ -24,4 +27,31 @@ export function Reset(ctx: PluginContext) { ...@@ -24,4 +27,31 @@ export function Reset(ctx: PluginContext) {
// TODO // TODO
// ctx.canvas3d.resetCamera(); // ctx.canvas3d.resetCamera();
}) })
}
export function SetSnapshot(ctx: PluginContext) {
PluginCommands.Camera.SetSnapshot.subscribe(ctx, ({ snapshot }) => {
ctx.canvas3d.camera.setState(snapshot);
ctx.canvas3d.requestDraw();
})
}
export function Snapshots(ctx: PluginContext) {
PluginCommands.Camera.Snapshots.Clear.subscribe(ctx, () => {
ctx.state.cameraSnapshots.clear();
});
PluginCommands.Camera.Snapshots.Remove.subscribe(ctx, ({ id }) => {
ctx.state.cameraSnapshots.remove(id);
});
PluginCommands.Camera.Snapshots.Add.subscribe(ctx, ({ name, description }) => {
const entry = CameraSnapshotManager.Entry(name || new Date().toLocaleTimeString(), ctx.canvas3d.camera.getSnapshot(), description);
ctx.state.cameraSnapshots.add(entry);
});
PluginCommands.Camera.Snapshots.Apply.subscribe(ctx, ({ id }) => {
const e = ctx.state.cameraSnapshots.getEntry(id);
return PluginCommands.Camera.SetSnapshot.dispatch(ctx, { snapshot: e.snapshot });
});
} }
\ No newline at end of file
...@@ -5,5 +5,14 @@ ...@@ -5,5 +5,14 @@
*/ */
import { PluginCommand } from './command'; import { PluginCommand } from './command';
import { Camera } from 'mol-canvas3d/camera';
export const Reset = PluginCommand<{}>({ isImmediate: true }); export const Reset = PluginCommand<{}>({ isImmediate: true });
\ No newline at end of file export const SetSnapshot = PluginCommand<{ snapshot: Camera.Snapshot }>({ isImmediate: true });
export const Snapshots = {
Add: PluginCommand<{ name?: string, description?: string }>({ isImmediate: true }),
Remove: PluginCommand<{ id: string }>({ isImmediate: true }),
Apply: PluginCommand<{ id: string }>({ isImmediate: true }),
Clear: PluginCommand<{ }>({ isImmediate: true }),
}
\ No newline at end of file
...@@ -28,7 +28,8 @@ export class PluginContext { ...@@ -28,7 +28,8 @@ export class PluginContext {
readonly events = { readonly events = {
state: { state: {
data: this.state.data.events, data: this.state.data.events,
behavior: this.state.behavior.events behavior: this.state.behavior.events,
cameraSnapshots: this.state.cameraSnapshots.events
} }
}; };
......
...@@ -8,17 +8,20 @@ import { State } from 'mol-state'; ...@@ -8,17 +8,20 @@ import { State } from 'mol-state';
import { PluginStateObject as SO } from './state/objects'; import { PluginStateObject as SO } from './state/objects';
import { Camera } from 'mol-canvas3d/camera'; import { Camera } from 'mol-canvas3d/camera';
import { PluginBehavior } from './behavior'; import { PluginBehavior } from './behavior';
import { CameraSnapshotManager } from './state/camera';
export { PluginState } export { PluginState }
class PluginState { class PluginState {
readonly data: State; readonly data: State;
readonly behavior: State; readonly behavior: State;
readonly cameraSnapshots: CameraSnapshotManager = new CameraSnapshotManager();
getSnapshot(): PluginState.Snapshot { getSnapshot(): PluginState.Snapshot {
return { return {
data: this.data.getSnapshot(), data: this.data.getSnapshot(),
behaviour: this.behavior.getSnapshot(), behaviour: this.behavior.getSnapshot(),
cameraSnapshots: this.cameraSnapshots.getStateSnapshot(),
canvas3d: { canvas3d: {
camera: this.plugin.canvas3d.camera.getSnapshot() camera: this.plugin.canvas3d.camera.getSnapshot()
} }
...@@ -28,12 +31,15 @@ class PluginState { ...@@ -28,12 +31,15 @@ class PluginState {
async setSnapshot(snapshot: PluginState.Snapshot) { async setSnapshot(snapshot: PluginState.Snapshot) {
await this.plugin.runTask(this.behavior.setSnapshot(snapshot.behaviour)); await this.plugin.runTask(this.behavior.setSnapshot(snapshot.behaviour));
await this.plugin.runTask(this.data.setSnapshot(snapshot.data)); await this.plugin.runTask(this.data.setSnapshot(snapshot.data));
this.cameraSnapshots.setStateSnapshot(snapshot.cameraSnapshots);
this.plugin.canvas3d.camera.setState(snapshot.canvas3d.camera); this.plugin.canvas3d.camera.setState(snapshot.canvas3d.camera);
this.plugin.canvas3d.requestDraw(true); this.plugin.canvas3d.requestDraw(true);
} }
dispose() { dispose() {
this.data.dispose(); this.data.dispose();
this.behavior.dispose();
this.cameraSnapshots.dispose();
} }
constructor(private plugin: import('./context').PluginContext) { constructor(private plugin: import('./context').PluginContext) {
...@@ -46,6 +52,7 @@ namespace PluginState { ...@@ -46,6 +52,7 @@ namespace PluginState {
export interface Snapshot { export interface Snapshot {
data: State.Snapshot, data: State.Snapshot,
behaviour: State.Snapshot, behaviour: State.Snapshot,
cameraSnapshots: CameraSnapshotManager.StateSnapshot,
canvas3d: { canvas3d: {
camera: Camera.Snapshot camera: Camera.Snapshot
} }
......
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Camera } from 'mol-canvas3d/camera';
import { OrderedMap } from 'immutable';
import { UUID } from 'mol-util';
import { RxEventHelper } from 'mol-util/rx-event-helper';
export { CameraSnapshotManager }
class CameraSnapshotManager {
private ev = RxEventHelper.create();
private _entries = OrderedMap<string, CameraSnapshotManager.Entry>().asMutable();
readonly events = {
changed: this.ev()
};
get entries() { return this._entries; }
getEntry(id: string) {
return this._entries.get(id);
}
remove(id: string) {
if (!this._entries.has(id)) return;
this._entries.delete(id);
this.events.changed.next();
}
add(e: CameraSnapshotManager.Entry) {
this._entries.set(e.id, e);
this.events.changed.next();
}
clear() {
if (this._entries.size === 0) return;
this._entries = OrderedMap<string, CameraSnapshotManager.Entry>().asMutable();
this.events.changed.next();
}
getStateSnapshot(): CameraSnapshotManager.StateSnapshot {
const entries: CameraSnapshotManager.Entry[] = [];
this._entries.forEach(e => entries.push(e!));
return { entries };
}
setStateSnapshot(state: CameraSnapshotManager.StateSnapshot ) {
this._entries = OrderedMap<string, CameraSnapshotManager.Entry>().asMutable();
for (const e of state.entries) {
this._entries.set(e.id, e);
}
this.events.changed.next();
}
dispose() {
this.ev.dispose();
}
}
namespace CameraSnapshotManager {
export interface Entry {
id: UUID,
name: string,
description?: string,
snapshot: Camera.Snapshot
}
export function Entry(name: string, snapshot: Camera.Snapshot, description?: string): Entry {
return { id: UUID.create22(), name, snapshot, description };
}
export interface StateSnapshot {
entries: Entry[]
}
}
\ No newline at end of file
...@@ -163,4 +163,66 @@ export class _test_UpdateTransform extends PluginComponent<{ state: State, nodeR ...@@ -163,4 +163,66 @@ export class _test_UpdateTransform extends PluginComponent<{ state: State, nodeR
<button onClick={() => this.update()} style={{ width: '100%' }}>Update</button> <button onClick={() => this.update()} style={{ width: '100%' }}>Update</button>
</div> </div>
} }
}
export class CameraSnapshots extends PluginComponent<{ }, { }> {
render() {
return <div>
<h3>Camera Snapshots</h3>
<CameraSnapshotControls />
<CameraSnapshotList />
</div>;
}
}
class CameraSnapshotControls extends PluginComponent<{ }, { name: string, description: string }> {
componentDidMount() {
this.subscribe(this.plugin.events.state.cameraSnapshots.changed, () => this.forceUpdate());
}
state = { name: '', description: '' };
add = () => {
PluginCommands.Camera.Snapshots.Add.dispatch(this.plugin, this.state);
this.setState({ name: '', description: '' })
}
clear = () => {
PluginCommands.Camera.Snapshots.Clear.dispatch(this.plugin, {});
}
render() {
return <div>
<input type='text' value={this.state.name} placeholder='Name...' style={{ width: '33%', display: 'block', float: 'left' }} onChange={e => this.setState({ name: e.target.value })} />
<input type='text' value={this.state.description} placeholder='Description...' style={{ width: '67%', display: 'block' }} onChange={e => this.setState({ description: e.target.value })} />
<button style={{ float: 'right' }} onClick={this.clear}>Clear</button>
<button onClick={this.add}>Add</button>
</div>;
}
}
class CameraSnapshotList extends PluginComponent<{ }, { }> {
componentDidMount() {
this.subscribe(this.plugin.events.state.cameraSnapshots.changed, () => this.forceUpdate());
}
apply(id: string) {
return () => PluginCommands.Camera.Snapshots.Apply.dispatch(this.plugin, { id });
}
remove(id: string) {
return () => {
PluginCommands.Camera.Snapshots.Remove.dispatch(this.plugin, { id });
}
}
render() {
return <ul style={{ listStyle: 'none' }}>
{this.plugin.state.cameraSnapshots.entries.valueSeq().map(e =><li key={e!.id}>
<button onClick={this.apply(e!.id)}>Set</button>
&nbsp;{e!.name} <small>{e!.description}</small>
<button onClick={this.remove(e!.id)} style={{ float: 'right' }}>X</button>
</li>)}
</ul>;
}
} }
\ No newline at end of file
...@@ -8,7 +8,7 @@ import * as React from 'react'; ...@@ -8,7 +8,7 @@ import * as React from 'react';
import { PluginContext } from '../context'; import { PluginContext } from '../context';
import { StateTree } from './state-tree'; import { StateTree } from './state-tree';
import { Viewport, ViewportControls } from './viewport'; import { Viewport, ViewportControls } from './viewport';
import { Controls, _test_UpdateTransform, _test_ApplyAction, _test_TrajectoryControls } from './controls'; import { Controls, _test_UpdateTransform, _test_ApplyAction, _test_TrajectoryControls, CameraSnapshots } from './controls';
import { PluginComponent, PluginReactContext } from './base'; import { PluginComponent, PluginReactContext } from './base';
import { merge } from 'rxjs'; import { merge } from 'rxjs';
import { State } from 'mol-state'; import { State } from 'mol-state';
...@@ -33,6 +33,8 @@ export class Plugin extends React.Component<{ plugin: PluginContext }, {}> { ...@@ -33,6 +33,8 @@ export class Plugin extends React.Component<{ plugin: PluginContext }, {}> {
<_test_CurrentObject /> <_test_CurrentObject />
<hr /> <hr />
<Controls /> <Controls />
<hr />
<CameraSnapshots />
</div> </div>
</div> </div>
</PluginReactContext.Provider>; </PluginReactContext.Provider>;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment