Browse Source

wip reworking snapshots

David Sehnal 6 years ago
parent
commit
907672dcc3

+ 46 - 15
src/mol-plugin/state.ts

@@ -14,6 +14,8 @@ import { RxEventHelper } from 'mol-util/rx-event-helper';
 import { Canvas3DProps } from 'mol-canvas3d/canvas3d';
 import { PluginCommands } from './command';
 import { PluginAnimationManager } from './state/animation/manager';
+import { ParamDefinition as PD } from 'mol-util/param-definition';
+import { UUID } from 'mol-util';
 export { PluginState }
 
 class PluginState {
@@ -40,31 +42,42 @@ class PluginState {
         }
     }
 
-    getSnapshot(): PluginState.Snapshot {
+    getSnapshot(params?: Partial<PluginState.GetSnapshotParams>): PluginState.Snapshot {
+        const p = { ...PluginState.DefaultGetSnapshotParams, ...params };
         return {
-            data: this.dataState.getSnapshot(),
-            behaviour: this.behaviorState.getSnapshot(),
-            animation: this.animation.getSnapshot(),
-            cameraSnapshots: this.cameraSnapshots.getStateSnapshot(),
-            canvas3d: {
-                camera: this.plugin.canvas3d.camera.getSnapshot(),
-                viewport: this.plugin.canvas3d.props
-            }
+            id: UUID.create22(),
+            data: p.data ? this.dataState.getSnapshot() : void 0,
+            behaviour: p.behavior ? this.behaviorState.getSnapshot() : void 0,
+            animation: p.animation ? this.animation.getSnapshot() : void 0,
+            camera: p.camera ? {
+                current: this.plugin.canvas3d.camera.getSnapshot(),
+                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) {
+        this.animation.stop();
+
         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.cameraSnapshots) this.cameraSnapshots.setStateSnapshot(snapshot.cameraSnapshots);
         if (snapshot.canvas3d) {
-            if (snapshot.canvas3d.viewport) PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: snapshot.canvas3d.viewport || { } });
-            if (snapshot.canvas3d.camera) this.plugin.canvas3d.camera.setState(snapshot.canvas3d.camera);
+            if (snapshot.canvas3d.props) await PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: snapshot.canvas3d.props || { } });
         }
-        this.plugin.canvas3d.requestDraw(true);
+        if (snapshot.cameraSnapshots) this.cameraSnapshots.setStateSnapshot(snapshot.cameraSnapshots);
         if (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() {
@@ -95,14 +108,32 @@ class PluginState {
 namespace PluginState {
     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 {
+        id: UUID,
         data?: State.Snapshot,
         behaviour?: State.Snapshot,
         animation?: PluginAnimationManager.Snapshot,
+        camera?: {
+            current: Camera.Snapshot,
+            transitionStyle: CameraTransitionStyle
+        },
         cameraSnapshots?: CameraSnapshotManager.StateSnapshot,
         canvas3d?: {
-            camera?: Camera.Snapshot,
-            viewport?: Canvas3DProps
+            props?: Canvas3DProps
         }
     }
 }

+ 5 - 2
src/mol-plugin/state/animation/manager.ts

@@ -103,8 +103,11 @@ class PluginAnimationManager extends PluginComponent<PluginAnimationManager.Stat
     stop() {
         this.context.canvas3d.setSceneAnimating(false);
         if (typeof this._frame !== 'undefined') cancelAnimationFrame(this._frame);
-        this.updateState({ animationState: 'stopped' });
-        this.triggerUpdate();
+
+        if (this.state.animationState !== 'stopped') {
+            this.updateState({ animationState: 'stopped' });
+            this.triggerUpdate();
+        }
     }
 
     get isAnimating() {

+ 54 - 9
src/mol-plugin/state/snapshots.ts

@@ -11,7 +11,7 @@ import { PluginComponent } from 'mol-plugin/component';
 
 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 = {
         changed: this.ev()
     };
@@ -22,40 +22,85 @@ class PluginStateSnapshotManager extends PluginComponent<{ entries: OrderedMap<s
 
     remove(id: string) {
         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();
     }
 
     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();
     }
 
     clear() {
         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();
     }
 
+    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() {
-        super({ entries: OrderedMap<string, PluginStateSnapshotManager.Entry>() });
+        super({ current: void 0, entries: OrderedMap<string, PluginStateSnapshotManager.Entry>() });
     }
 }
 
 namespace PluginStateSnapshotManager {
     export interface Entry {
-        id: UUID,
-        timestamp: string,
+        timestamp: number,
         name?: string,
         description?: string,
         snapshot: PluginState.Snapshot
     }
 
     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[]
     }
 }

+ 1 - 1
src/mol-plugin/state/transforms/representation.ts

@@ -161,7 +161,7 @@ const StructureRepresentation3D = PluginStateTransform.BuiltIn({
 })({
     canAutoUpdate({ a, oldParams, newParams }) {
         // 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) {
         return Task.create('Structure Representation', async ctx => {

+ 3 - 3
src/mol-plugin/ui/state.tsx

@@ -110,9 +110,9 @@ class LocalStateSnapshotList extends PluginUIComponent<{ }, { }> {
 
     render() {
         return <ul style={{ listStyle: 'none' }} className='msp-state-list'>
-            {this.plugin.state.snapshots.state.entries.valueSeq().map(e =><li key={e!.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 onClick={this.remove(e!.id)} className='msp-btn msp-btn-link msp-state-list-remove-button'>
+            {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!.snapshot.id)}>{e!.name || new Date(e!.timestamp).toLocaleString()} <small>{e!.description}</small></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' />
                 </button>
             </li>)}