state.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { State, StateTransform, StateTransformer } from '../mol-state';
  7. import { PluginStateObject as SO } from '../mol-plugin-state/objects';
  8. import { Camera } from '../mol-canvas3d/camera';
  9. import { PluginBehavior } from './behavior';
  10. import { CameraSnapshotManager } from '../mol-plugin-state/camera';
  11. import { PluginStateSnapshotManager } from '../mol-plugin-state/snapshots';
  12. import { RxEventHelper } from '../mol-util/rx-event-helper';
  13. import { Canvas3DProps } from '../mol-canvas3d/canvas3d';
  14. import { PluginCommands } from './commands';
  15. import { PluginAnimationManager } from '../mol-plugin-state/animation/manager';
  16. import { ParamDefinition as PD } from '../mol-util/param-definition';
  17. import { UUID } from '../mol-util';
  18. import { InteractivityManager } from '../mol-plugin-state/manager/interactivity';
  19. export { PluginState }
  20. class PluginState {
  21. private ev = RxEventHelper.create();
  22. readonly data: State;
  23. readonly behaviors: State;
  24. readonly animation: PluginAnimationManager;
  25. readonly cameraSnapshots = new CameraSnapshotManager();
  26. readonly snapshots: PluginStateSnapshotManager;
  27. readonly behavior = {
  28. kind: this.ev.behavior<PluginState.Kind>('data'),
  29. currentObject: this.ev.behavior<State.ObjectEvent>({} as any)
  30. }
  31. setKind(kind: PluginState.Kind) {
  32. const current = this.behavior.kind.value;
  33. if (kind !== current) {
  34. this.behavior.kind.next(kind);
  35. this.behavior.currentObject.next(kind === 'data'
  36. ? this.data.behaviors.currentObject.value
  37. : this.behaviors.behaviors.currentObject.value)
  38. }
  39. }
  40. getSnapshot(params?: PluginState.GetSnapshotParams): PluginState.Snapshot {
  41. const p = { ...PluginState.DefaultGetSnapshotParams, ...params };
  42. return {
  43. id: UUID.create22(),
  44. data: p.data ? this.data.getSnapshot() : void 0,
  45. behaviour: p.behavior ? this.behaviors.getSnapshot() : void 0,
  46. animation: p.animation ? this.animation.getSnapshot() : void 0,
  47. startAnimation: p.startAnimation ? !!p.startAnimation : void 0,
  48. camera: p.camera ? {
  49. current: this.plugin.canvas3d!.camera.getSnapshot(),
  50. transitionStyle: p.cameraTranstion.name,
  51. transitionDurationInMs: (params && params.cameraTranstion && params.cameraTranstion.name === 'animate') ? params.cameraTranstion.params.durationInMs : undefined
  52. } : void 0,
  53. cameraSnapshots: p.cameraSnapshots ? this.cameraSnapshots.getStateSnapshot() : void 0,
  54. canvas3d: p.canvas3d ? { props: this.plugin.canvas3d?.props } : void 0,
  55. interactivity: p.interactivity ? { props: this.plugin.managers.interactivity.props } : void 0,
  56. durationInMs: params && params.durationInMs
  57. };
  58. }
  59. async setSnapshot(snapshot: PluginState.Snapshot) {
  60. await this.animation.stop();
  61. if (snapshot.behaviour) await this.plugin.runTask(this.behaviors.setSnapshot(snapshot.behaviour));
  62. if (snapshot.data) await this.plugin.runTask(this.data.setSnapshot(snapshot.data));
  63. if (snapshot.canvas3d) {
  64. if (snapshot.canvas3d.props) await PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: snapshot.canvas3d.props });
  65. }
  66. if (snapshot.interactivity) {
  67. if (snapshot.interactivity.props) this.plugin.managers.interactivity.setProps(snapshot.interactivity.props);
  68. }
  69. if (snapshot.cameraSnapshots) this.cameraSnapshots.setStateSnapshot(snapshot.cameraSnapshots);
  70. if (snapshot.animation) {
  71. this.animation.setSnapshot(snapshot.animation);
  72. }
  73. if (snapshot.camera) {
  74. PluginCommands.Camera.Reset(this.plugin, {
  75. snapshot: snapshot.camera.current,
  76. durationMs: snapshot.camera.transitionStyle === 'animate'
  77. ? snapshot.camera.transitionDurationInMs
  78. : void 0
  79. });
  80. }
  81. if (snapshot.startAnimation) {
  82. this.animation.start();
  83. }
  84. }
  85. applyTransform(state: State, a: StateTransform.Ref, transformer: StateTransformer, params: any) {
  86. const tree = state.build().to(a).apply(transformer, params);
  87. return PluginCommands.State.Update(this.plugin, { state, tree });
  88. }
  89. updateTransform(state: State, a: StateTransform.Ref, params: any, canUndo?: string | boolean) {
  90. const tree = state.build().to(a).update(params);
  91. return PluginCommands.State.Update(this.plugin, { state, tree, options: { canUndo } });
  92. }
  93. dispose() {
  94. this.ev.dispose();
  95. this.data.dispose();
  96. this.behaviors.dispose();
  97. this.cameraSnapshots.dispose();
  98. this.animation.dispose();
  99. }
  100. constructor(private plugin: import('./context').PluginContext) {
  101. this.snapshots = new PluginStateSnapshotManager(plugin);
  102. this.data = State.create(new SO.Root({ }), { globalContext: plugin });
  103. this.behaviors = State.create(new PluginBehavior.Root({ }), { globalContext: plugin, rootState: { isLocked: true } });
  104. this.data.behaviors.currentObject.subscribe(o => {
  105. if (this.behavior.kind.value === 'data') this.behavior.currentObject.next(o);
  106. });
  107. this.behaviors.behaviors.currentObject.subscribe(o => {
  108. if (this.behavior.kind.value === 'behavior') this.behavior.currentObject.next(o);
  109. });
  110. this.behavior.currentObject.next(this.data.behaviors.currentObject.value);
  111. this.animation = new PluginAnimationManager(plugin);
  112. }
  113. }
  114. namespace PluginState {
  115. export type Kind = 'data' | 'behavior'
  116. export type CameraTransitionStyle = 'instant' | 'animate'
  117. export const GetSnapshotParams = {
  118. durationInMs: PD.Numeric(1500, { min: 100, max: 15000, step: 100 }, { label: 'Duration in ms' }),
  119. data: PD.Boolean(true),
  120. behavior: PD.Boolean(false),
  121. animation: PD.Boolean(true),
  122. startAnimation: PD.Boolean(false),
  123. canvas3d: PD.Boolean(true),
  124. interactivity: PD.Boolean(true),
  125. camera: PD.Boolean(true),
  126. // TODO: make camera snapshots same as the StateSnapshots with "child states?"
  127. cameraSnapshots: PD.Boolean(false),
  128. cameraTranstion: PD.MappedStatic('animate', {
  129. animate: PD.Group({
  130. durationInMs: PD.Numeric(250, { min: 100, max: 5000, step: 500 }, { label: 'Duration in ms' }),
  131. }),
  132. instant: PD.Group({ })
  133. }, { options: [['animate', 'Animate'], ['instant', 'Instant']] })
  134. };
  135. export type GetSnapshotParams = Partial<PD.Values<typeof GetSnapshotParams>>
  136. export const DefaultGetSnapshotParams = PD.getDefaultValues(GetSnapshotParams);
  137. export interface Snapshot {
  138. id: UUID,
  139. data?: State.Snapshot,
  140. behaviour?: State.Snapshot,
  141. animation?: PluginAnimationManager.Snapshot,
  142. startAnimation?: boolean,
  143. camera?: {
  144. current: Camera.Snapshot,
  145. transitionStyle: CameraTransitionStyle,
  146. transitionDurationInMs?: number
  147. },
  148. cameraSnapshots?: CameraSnapshotManager.StateSnapshot,
  149. canvas3d?: {
  150. props?: Canvas3DProps
  151. },
  152. interactivity?: {
  153. props?: InteractivityManager.Props
  154. },
  155. durationInMs?: number
  156. }
  157. }