Ver Fonte

CameraManager

David Sehnal há 5 anos atrás
pai
commit
db7585b6b6

+ 56 - 0
src/mol-plugin-state/manager/camera.ts

@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { Sphere3D } from '../../mol-math/geometry';
+import { StructureElement } from '../../mol-model/structure';
+import { PluginContext } from '../../mol-plugin/context';
+import { PrincipalAxes } from '../../mol-math/linear-algebra/matrix/principal-axes';
+import { Camera } from '../../mol-canvas3d/camera';
+
+// TODO: make this customizable somewhere?
+const DefaultCameraFocusOptions = {
+    minRadius: 5,
+    extraRadius: 6,
+    durationMs: 250
+}
+
+export type CameraFocusOptions = typeof DefaultCameraFocusOptions
+
+export class CameraManager {
+    focusLoci(loci: StructureElement.Loci, options?: Partial<CameraFocusOptions>) {
+        // TODO: allow computation of principal axes here?
+        // perhaps have an optimized function, that does exact axes small Loci and approximate/sampled from big ones?        
+
+        const { extraRadius, minRadius, durationMs } = { ...DefaultCameraFocusOptions, ...options };
+        const { sphere } = StructureElement.Loci.getBoundary(loci);
+        const radius = Math.max(sphere.radius + extraRadius, minRadius);
+        this.plugin.canvas3d?.camera.focus(sphere.center, radius, this.plugin.canvas3d.boundingSphere.radius, durationMs);
+    }
+
+    focusSphere(sphere: Sphere3D, options?: Partial<CameraFocusOptions> & { principalAxes?: PrincipalAxes }) {
+        const { extraRadius, minRadius, durationMs } = { ...DefaultCameraFocusOptions, ...options };
+        const radius = Math.max(sphere.radius + extraRadius, minRadius);
+
+        if (options?.principalAxes) {
+            const { origin, dirA, dirC } = options?.principalAxes.boxAxes;
+            this.plugin.canvas3d?.camera.focus(origin, radius, this.plugin.canvas3d.boundingSphere.radius, durationMs, dirA, dirC);
+        } else {
+            this.plugin.canvas3d?.camera.focus(sphere.center, radius, this.plugin.canvas3d.boundingSphere.radius, durationMs);
+        }
+    }
+
+    setSnapshot(snapshot: Partial<Camera.Snapshot>, durationMs?: number) {
+        // TODO: setState and requestCameraReset are very similar now: unify them?        
+        this.plugin.canvas3d?.camera.setState(snapshot, durationMs);
+    }
+
+    reset(snapshot?: Partial<Camera.Snapshot>, durationMs?: number) {
+        this.plugin.canvas3d?.requestCameraReset({ snapshot, durationMs });
+    }
+
+    constructor(readonly plugin: PluginContext) {     
+    }
+}

+ 8 - 25
src/mol-plugin-state/manager/interactivity.ts

@@ -5,20 +5,18 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { Loci as ModelLoci, EmptyLoci, EveryLoci, isEmptyLoci } from '../../mol-model/loci';
-import { ModifiersKeys, ButtonsType } from '../../mol-util/input/input-observer';
+import { EmptyLoci, EveryLoci, isEmptyLoci, Loci as ModelLoci } from '../../mol-model/loci';
+import { Structure, StructureElement } from '../../mol-model/structure';
+import { PluginContext } from '../../mol-plugin/context';
 import { Representation } from '../../mol-repr/representation';
-import { StructureElement } from '../../mol-model/structure';
+import { ButtonsType, ModifiersKeys } from '../../mol-util/input/input-observer';
 import { MarkerAction } from '../../mol-util/marker-action';
-import { PluginContext } from '../../mol-plugin/context';
-import { Structure } from '../../mol-model/structure';
+import { shallowEqual } from '../../mol-util/object';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
-import { StructureSelectionManager } from './structure/selection';
 import { PluginComponent } from '../component';
-import { shallowEqual } from '../../mol-util/object';
-import { Sphere3D } from '../../mol-math/geometry';
+import { StructureSelectionManager } from './structure/selection';
 
-export { InteractivityManager }
+export { InteractivityManager };
 
 interface InteractivityManagerState {
     props: PD.ValuesFor<InteractivityManager.Params>
@@ -56,21 +54,6 @@ class InteractivityManager extends PluginComponent<InteractivityManagerState> {
         this.events.propsUpdated.next();
     }
 
-    focusLoci(loci: StructureElement.Loci, options?: InteractivityFocusLociOptions) {
-        const { extraRadius, minRadius, durationMs } = { ...DefaultInteractivityFocusOptions, ...options };
-        const { sphere } = StructureElement.Loci.getBoundary(loci);
-        const radius = Math.max(sphere.radius + extraRadius, minRadius);
-        this.plugin.canvas3d?.camera.focus(sphere.center, radius, this.plugin.canvas3d.boundingSphere.radius, durationMs);
-    }
-
-    focusSphere(sphere: Sphere3D, options?: InteractivityFocusLociOptions) {
-        if (sphere) {
-            const { extraRadius, minRadius, durationMs } = { ...DefaultInteractivityFocusOptions, ...options };
-            const radius = Math.max(sphere.radius + extraRadius, minRadius);
-            this.plugin.canvas3d?.camera.focus(sphere.center, radius, this.plugin.canvas3d.boundingSphere.radius, durationMs);
-        }
-    }
-
     constructor(readonly plugin: PluginContext, props: Partial<InteractivityManager.Props> = {}) {
         super({ props: { ...PD.getDefaultValues(InteractivityManager.Params), ...props } });
 
@@ -90,7 +73,7 @@ namespace InteractivityManager {
     }
 
     export const Params = {
-        granularity: PD.Select('residue', ModelLoci.GranularityOptions, { label: 'Picking', description: 'Controls if selections are expanded upon picking to whole residues, chains, structures, instances, or left as atoms and coarse elements' }),
+        granularity: PD.Select('residue', ModelLoci.GranularityOptions, { label: 'Picking Level', description: 'Controls if selections are expanded upon picking to whole residues, chains, structures, instances, or left as atoms and coarse elements' }),
     }
     export type Params = typeof Params
     export type Props = PD.Values<Params>

+ 1 - 1
src/mol-plugin-ui/structure/components.tsx

@@ -303,7 +303,7 @@ class StructureComponentGroup extends PurePluginUIComponent<{ group: StructureCo
 
     focus = () => {
         const sphere = this.pivot.cell.obj?.data.boundary.sphere;
-        if (sphere) this.plugin.managers.interactivity.focusSphere(sphere);
+        if (sphere) this.plugin.managers.camera.focusSphere(sphere);
     }
 
     render() {

+ 5 - 8
src/mol-plugin-ui/structure/measurements.tsx

@@ -14,7 +14,7 @@ import { PluginCommands } from '../../mol-plugin/commands';
 import { State } from '../../mol-state';
 import { angleLabel, dihedralLabel, distanceLabel, lociLabel } from '../../mol-theme/label';
 import { FiniteArray } from '../../mol-util/type-helpers';
-import { CollapsableControls, CollapsableState, PurePluginUIComponent } from '../base';
+import { CollapsableControls, PurePluginUIComponent } from '../base';
 import { ActionMenu } from '../controls/action-menu';
 import { ExpandGroup, IconButton, ToggleButton } from '../controls/common';
 import { Icon } from '../controls/icons';
@@ -22,15 +22,12 @@ import { ParameterControls } from '../controls/parameters';
 
 // TODO details, options (e.g. change text for labels)
 
-interface StructureMeasurementsControlsState extends CollapsableState {
-}
-
-export class StructureMeasurementsControls extends CollapsableControls<{}, StructureMeasurementsControlsState> {
+export class StructureMeasurementsControls extends CollapsableControls {
     defaultState() {
         return {
             isCollapsed: false,
             header: 'Measurements',
-        } as StructureMeasurementsControlsState
+        };
     }
 
     renderControls() {
@@ -142,7 +139,7 @@ export class MeasurementControls extends PurePluginUIComponent<{}, { isBusy: boo
     }
 
     focusLoci(loci: StructureElement.Loci) {
-        this.plugin.managers.interactivity.focusLoci(loci);
+        this.plugin.managers.camera.focusLoci(loci);
     }
 
     historyEntry(e: StructureSelectionHistoryEntry, idx: number) {
@@ -258,7 +255,7 @@ class MeasurementEntry extends PurePluginUIComponent<{ cell: StructureMeasuremen
 
         const sphere = Loci.getBundleBoundingSphere(toLociBundle(selections.data))
         if (sphere) {
-            this.plugin.managers.interactivity.focusSphere(sphere);
+            this.plugin.managers.camera.focusSphere(sphere);
         }
     }
 

+ 3 - 14
src/mol-plugin-ui/structure/selection.tsx

@@ -31,10 +31,6 @@ const StructureSelectionParams = {
 }
 
 interface StructureSelectionControlsState extends CollapsableState {
-    minRadius: number,
-    extraRadius: number,
-    durationMs: number,
-
     isEmpty: boolean,
     isBusy: boolean,
 
@@ -84,13 +80,10 @@ export class StructureSelectionControls<P, S extends StructureSelectionControlsS
     }
 
     focus = () => {
-        const { extraRadius, minRadius, durationMs } = this.state
-        if (this.plugin.managers.structure.selection.stats.elementCount === 0) return
+        if (this.plugin.managers.structure.selection.stats.elementCount === 0) return;
         const principalAxes = this.plugin.managers.structure.selection.getPrincipalAxes();
-        const { origin, dirA, dirC } = principalAxes.boxAxes
         const { sphere } = this.plugin.managers.structure.selection.getBoundary()
-        const radius = Math.max(sphere.radius + extraRadius, minRadius);
-        this.plugin.canvas3d?.camera.focus(origin, radius, this.plugin.canvas3d.boundingSphere.radius, durationMs, dirA, dirC);
+        this.plugin.managers.camera.focusSphere(sphere, { principalAxes });
     }
 
     setProps = (props: any) => {
@@ -151,10 +144,6 @@ export class StructureSelectionControls<P, S extends StructureSelectionControlsS
             isCollapsed: false,
             header: 'Selection',
 
-            minRadius: 8,
-            extraRadius: 4,
-            durationMs: 250,
-
             action: void 0,
 
             isEmpty: true,
@@ -167,7 +156,7 @@ export class StructureSelectionControls<P, S extends StructureSelectionControlsS
             <ParameterControls params={StructureSelectionParams} values={this.values} onChangeObject={this.setProps} />
             {this.controls}
             <div className='msp-control-row msp-row-text' style={{ marginTop: '6px' }}>
-                <button className='msp-btn msp-btn-block' onClick={this.focus}>
+                <button className='msp-btn msp-btn-block' onClick={this.focus} title='Click to Focus Selection'>
                     <Icon name='focus-on-visual' style={{ position: 'absolute', left: '5px' }} />
                     {this.stats}
                 </button>

+ 3 - 3
src/mol-plugin/behavior/static/camera.ts

@@ -17,19 +17,19 @@ export function registerDefault(ctx: PluginContext) {
 
 export function Reset(ctx: PluginContext) {
     PluginCommands.Camera.Reset.subscribe(ctx, options => {
-        ctx.canvas3d?.requestCameraReset(options);
+        ctx.managers.camera.reset(options?.snapshot, options?.durationMs);
     })
 }
 
 export function SetSnapshot(ctx: PluginContext) {
     PluginCommands.Camera.SetSnapshot.subscribe(ctx, ({ snapshot, durationMs }) => {
-        ctx.canvas3d?.camera.transition.apply(snapshot, durationMs);
+        ctx.managers.camera.setSnapshot(snapshot, durationMs);
     })
 }
 
 export function Focus(ctx: PluginContext) {
     PluginCommands.Camera.Focus.subscribe(ctx, ({ center, radius, durationMs }) => {
-        ctx.canvas3d?.camera.focus(center, radius, ctx.canvas3d?.boundingSphere.radius, durationMs);
+        ctx.managers.camera.focusSphere({ center, radius }, { durationMs });
     })
 }
 

+ 2 - 0
src/mol-plugin/context.ts

@@ -47,6 +47,7 @@ import { StructureHierarchyManager } from '../mol-plugin-state/manager/structure
 import { StructureSelectionManager } from '../mol-plugin-state/manager/structure/selection';
 import { TrajectoryFormatRegistry } from '../mol-plugin-state/formats/trajectory';
 import { StructureComponentManager } from '../mol-plugin-state/manager/structure/component';
+import { CameraManager } from '../mol-plugin-state/manager/camera';
 
 export class PluginContext {
     private disposed = false;
@@ -130,6 +131,7 @@ export class PluginContext {
             selection: new StructureSelectionManager(this)
         },
         interactivity: void 0 as any as InteractivityManager,
+        camera: new CameraManager(this),
         lociLabels: void 0 as any as LociLabelManager,
         toast: new PluginToastManager(this)
     } as const