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

wip reworking snapshots

parent 017c0ba8
Branches
Tags
No related merge requests found
...@@ -14,6 +14,8 @@ import { RxEventHelper } from 'mol-util/rx-event-helper'; ...@@ -14,6 +14,8 @@ import { RxEventHelper } from 'mol-util/rx-event-helper';
import { Canvas3DProps } from 'mol-canvas3d/canvas3d'; import { Canvas3DProps } from 'mol-canvas3d/canvas3d';
import { PluginCommands } from './command'; import { PluginCommands } from './command';
import { PluginAnimationManager } from './state/animation/manager'; import { PluginAnimationManager } from './state/animation/manager';
import { ParamDefinition as PD } from 'mol-util/param-definition';
import { UUID } from 'mol-util';
export { PluginState } export { PluginState }
class PluginState { class PluginState {
...@@ -40,31 +42,42 @@ class PluginState { ...@@ -40,31 +42,42 @@ class PluginState {
} }
} }
getSnapshot(): PluginState.Snapshot { getSnapshot(params?: Partial<PluginState.GetSnapshotParams>): PluginState.Snapshot {
const p = { ...PluginState.DefaultGetSnapshotParams, ...params };
return { return {
data: this.dataState.getSnapshot(), id: UUID.create22(),
behaviour: this.behaviorState.getSnapshot(), data: p.data ? this.dataState.getSnapshot() : void 0,
animation: this.animation.getSnapshot(), behaviour: p.behavior ? this.behaviorState.getSnapshot() : void 0,
cameraSnapshots: this.cameraSnapshots.getStateSnapshot(), animation: p.animation ? this.animation.getSnapshot() : void 0,
canvas3d: { camera: p.camera ? {
camera: this.plugin.canvas3d.camera.getSnapshot(), current: this.plugin.canvas3d.camera.getSnapshot(),
viewport: this.plugin.canvas3d.props transitionStyle: p.cameraTranstionStyle ? p.cameraTranstionStyle : 'instant'
} } : void 0,
cameraSnapshots: p.cameraSnapshots ? this.cameraSnapshots.getStateSnapshot() : void 0,
canvas3d: p.canvas3d ? {
props: this.plugin.canvas3d.props
} : void 0
}; };
} }
async setSnapshot(snapshot: PluginState.Snapshot) { async setSnapshot(snapshot: PluginState.Snapshot) {
this.animation.stop();
if (snapshot.behaviour) await this.plugin.runTask(this.behaviorState.setSnapshot(snapshot.behaviour)); if (snapshot.behaviour) await this.plugin.runTask(this.behaviorState.setSnapshot(snapshot.behaviour));
if (snapshot.data) await this.plugin.runTask(this.dataState.setSnapshot(snapshot.data)); if (snapshot.data) await this.plugin.runTask(this.dataState.setSnapshot(snapshot.data));
if (snapshot.cameraSnapshots) this.cameraSnapshots.setStateSnapshot(snapshot.cameraSnapshots);
if (snapshot.canvas3d) { if (snapshot.canvas3d) {
if (snapshot.canvas3d.viewport) PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: snapshot.canvas3d.viewport || { } }); if (snapshot.canvas3d.props) await PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: snapshot.canvas3d.props || { } });
if (snapshot.canvas3d.camera) this.plugin.canvas3d.camera.setState(snapshot.canvas3d.camera);
} }
this.plugin.canvas3d.requestDraw(true); if (snapshot.cameraSnapshots) this.cameraSnapshots.setStateSnapshot(snapshot.cameraSnapshots);
if (snapshot.animation) { if (snapshot.animation) {
this.animation.setSnapshot(snapshot.animation); this.animation.setSnapshot(snapshot.animation);
} }
if (snapshot.camera) {
await PluginCommands.Camera.SetSnapshot.dispatch(this.plugin, {
snapshot: snapshot.camera.current,
durationMs: snapshot.camera.transitionStyle === 'animate' ? 250 : void 0
});
}
} }
dispose() { dispose() {
...@@ -95,14 +108,32 @@ class PluginState { ...@@ -95,14 +108,32 @@ class PluginState {
namespace PluginState { namespace PluginState {
export type Kind = 'data' | 'behavior' export type Kind = 'data' | 'behavior'
export type CameraTransitionStyle = 'instant' | 'animate'
export const GetSnapshotParams = {
data: PD.Boolean(true),
behavior: PD.Boolean(false),
animation: PD.Boolean(true),
canvas3d: PD.Boolean(true),
camera: PD.Boolean(true),
// TODO: make camera snapshots same as the StateSnapshots with "child states?"
cameraSnapshots: PD.Boolean(false),
cameraTranstionStyle: PD.Select<CameraTransitionStyle>('animate', [['animate', 'Animate'], ['instant', 'Instant']])
};
export type GetSnapshotParams = PD.Value<typeof GetSnapshotParams>
export const DefaultGetSnapshotParams = PD.getDefaultValues(GetSnapshotParams);
export interface Snapshot { export interface Snapshot {
id: UUID,
data?: State.Snapshot, data?: State.Snapshot,
behaviour?: State.Snapshot, behaviour?: State.Snapshot,
animation?: PluginAnimationManager.Snapshot, animation?: PluginAnimationManager.Snapshot,
camera?: {
current: Camera.Snapshot,
transitionStyle: CameraTransitionStyle
},
cameraSnapshots?: CameraSnapshotManager.StateSnapshot, cameraSnapshots?: CameraSnapshotManager.StateSnapshot,
canvas3d?: { canvas3d?: {
camera?: Camera.Snapshot, props?: Canvas3DProps
viewport?: Canvas3DProps
} }
} }
} }
...@@ -103,9 +103,12 @@ class PluginAnimationManager extends PluginComponent<PluginAnimationManager.Stat ...@@ -103,9 +103,12 @@ class PluginAnimationManager extends PluginComponent<PluginAnimationManager.Stat
stop() { stop() {
this.context.canvas3d.setSceneAnimating(false); this.context.canvas3d.setSceneAnimating(false);
if (typeof this._frame !== 'undefined') cancelAnimationFrame(this._frame); if (typeof this._frame !== 'undefined') cancelAnimationFrame(this._frame);
if (this.state.animationState !== 'stopped') {
this.updateState({ animationState: 'stopped' }); this.updateState({ animationState: 'stopped' });
this.triggerUpdate(); this.triggerUpdate();
} }
}
get isAnimating() { get isAnimating() {
return this.state.animationState === 'playing'; return this.state.animationState === 'playing';
......
...@@ -11,7 +11,7 @@ import { PluginComponent } from 'mol-plugin/component'; ...@@ -11,7 +11,7 @@ import { PluginComponent } from 'mol-plugin/component';
export { PluginStateSnapshotManager } export { PluginStateSnapshotManager }
class PluginStateSnapshotManager extends PluginComponent<{ entries: OrderedMap<string, PluginStateSnapshotManager.Entry> }> { class PluginStateSnapshotManager extends PluginComponent<{ current?: UUID | undefined, entries: OrderedMap<string, PluginStateSnapshotManager.Entry> }> {
readonly events = { readonly events = {
changed: this.ev() changed: this.ev()
}; };
...@@ -22,40 +22,85 @@ class PluginStateSnapshotManager extends PluginComponent<{ entries: OrderedMap<s ...@@ -22,40 +22,85 @@ class PluginStateSnapshotManager extends PluginComponent<{ entries: OrderedMap<s
remove(id: string) { remove(id: string) {
if (!this.state.entries.has(id)) return; if (!this.state.entries.has(id)) return;
this.updateState({ entries: this.state.entries.delete(id) }); this.updateState({
current: this.state.current === id ? void 0 : this.state.current,
entries: this.state.entries.delete(id)
});
this.events.changed.next(); this.events.changed.next();
} }
add(e: PluginStateSnapshotManager.Entry) { add(e: PluginStateSnapshotManager.Entry) {
this.updateState({ entries: this.state.entries.set(e.id, e) }); this.updateState({ current: e.snapshot.id, entries: this.state.entries.set(e.snapshot.id, e) });
this.events.changed.next(); this.events.changed.next();
} }
clear() { clear() {
if (this.state.entries.size === 0) return; if (this.state.entries.size === 0) return;
this.updateState({ entries: OrderedMap<string, PluginStateSnapshotManager.Entry>() }); this.updateState({ current: void 0, entries: OrderedMap<string, PluginStateSnapshotManager.Entry>() });
this.events.changed.next(); this.events.changed.next();
} }
setCurrent(id: string) {
const e = this.getEntry(id);
if (e) {
this.updateState({ current: id as UUID });
this.events.changed.next();
}
return e && e.snapshot;
}
setRemoteSnapshot(snapshot: PluginStateSnapshotManager.RemoteSnapshot): PluginState.Snapshot | undefined {
this.clear();
const entries = this.state.entries.withMutations(m => {
for (const e of snapshot.entries) {
m.set(e.snapshot.id, e);
}
});
const current = snapshot.current
? snapshot.current
: snapshot.entries.length > 0
? snapshot.entries[0].snapshot.id
: void 0;
this.updateState({ current, entries });
this.events.changed.next();
if (!current) return;
const ret = this.getEntry(current);
return ret && ret.snapshot;
}
getRemoteSnapshot(options?: { name?: string, description?: string }): PluginStateSnapshotManager.RemoteSnapshot {
// TODO: diffing and all that fancy stuff
return {
timestamp: +new Date(),
name: options && options.name,
description: options && options.description,
current: this.state.current,
entries: this.state.entries.valueSeq().toArray()
};
}
constructor() { constructor() {
super({ entries: OrderedMap<string, PluginStateSnapshotManager.Entry>() }); super({ current: void 0, entries: OrderedMap<string, PluginStateSnapshotManager.Entry>() });
} }
} }
namespace PluginStateSnapshotManager { namespace PluginStateSnapshotManager {
export interface Entry { export interface Entry {
id: UUID, timestamp: number,
timestamp: string,
name?: string, name?: string,
description?: string, description?: string,
snapshot: PluginState.Snapshot snapshot: PluginState.Snapshot
} }
export function Entry(snapshot: PluginState.Snapshot, name?: string, description?: string): Entry { export function Entry(snapshot: PluginState.Snapshot, name?: string, description?: string): Entry {
return { id: UUID.create22(), timestamp: new Date().toLocaleString(), name, snapshot, description }; return { timestamp: +new Date(), name, snapshot, description };
} }
export interface StateSnapshot { export interface RemoteSnapshot {
timestamp: number,
name?: string,
description?: string,
current: UUID | undefined,
entries: Entry[] entries: Entry[]
} }
} }
\ No newline at end of file
...@@ -161,7 +161,7 @@ const StructureRepresentation3D = PluginStateTransform.BuiltIn({ ...@@ -161,7 +161,7 @@ const StructureRepresentation3D = PluginStateTransform.BuiltIn({
})({ })({
canAutoUpdate({ a, oldParams, newParams }) { canAutoUpdate({ a, oldParams, newParams }) {
// TODO: other criteria as well? // TODO: other criteria as well?
return a.data.elementCount < 10000 && oldParams.type.name === newParams.type.name; return a.data.elementCount < 10000 || oldParams.type.name === newParams.type.name;
}, },
apply({ a, params }, plugin: PluginContext) { apply({ a, params }, plugin: PluginContext) {
return Task.create('Structure Representation', async ctx => { return Task.create('Structure Representation', async ctx => {
......
...@@ -110,9 +110,9 @@ class LocalStateSnapshotList extends PluginUIComponent<{ }, { }> { ...@@ -110,9 +110,9 @@ class LocalStateSnapshotList extends PluginUIComponent<{ }, { }> {
render() { render() {
return <ul style={{ listStyle: 'none' }} className='msp-state-list'> return <ul style={{ listStyle: 'none' }} className='msp-state-list'>
{this.plugin.state.snapshots.state.entries.valueSeq().map(e =><li key={e!.id}> {this.plugin.state.snapshots.state.entries.valueSeq().map(e =><li key={e!.snapshot.id}>
<button className='msp-btn msp-btn-block msp-form-control' onClick={this.apply(e!.id)}>{e!.name || e!.timestamp} <small>{e!.description}</small></button> <button className='msp-btn msp-btn-block msp-form-control' onClick={this.apply(e!.snapshot.id)}>{e!.name || new Date(e!.timestamp).toLocaleString()} <small>{e!.description}</small></button>
<button onClick={this.remove(e!.id)} className='msp-btn msp-btn-link msp-state-list-remove-button'> <button onClick={this.remove(e!.snapshot.id)} className='msp-btn msp-btn-link msp-state-list-remove-button'>
<span className='msp-icon msp-icon-remove' /> <span className='msp-icon msp-icon-remove' />
</button> </button>
</li>)} </li>)}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment