Parcourir la source

add volume controls to Volseg extension

dsehnal il y a 2 ans
Parent
commit
1b382653f2

+ 1 - 1
CHANGELOG.md

@@ -8,7 +8,7 @@ Note that since we don't clearly distinguish between a public and private interf
 
 - Show histogram in direct volume control point settings
 - Add `solidInterior` parameter to sphere/cylinder impostors
-- Add `meshes` and `volseg` extensions
+- Add `meshes` and `volumes-and-segmentations` extensions
 
 ## [v3.27.0] - 2022-12-15
 

+ 12 - 1
src/extensions/volumes-and-segmentations/entry-root.ts

@@ -27,11 +27,12 @@ import { VolsegMeshSegmentationData } from './entry-meshes';
 import { VolsegModelData } from './entry-models';
 import { VolsegLatticeSegmentationData } from './entry-segmentation';
 import { VolsegState, VolsegStateData, VolsegStateParams } from './entry-state';
-import { VolsegVolumeData, SimpleVolumeParamValues } from './entry-volume';
+import { VolsegVolumeData, SimpleVolumeParamValues, VOLUME_VISUAL_TAG } from './entry-volume';
 import * as ExternalAPIs from './external-api';
 import { VolsegGlobalStateData } from './global-state';
 import { applyEllipsis, Choice, isDefined, lazyGetter, splitEntryId } from './helpers';
 import { type VolsegStateFromEntry } from './transformers';
+import { StateTransforms } from '../../mol-plugin-state/transforms';
 
 
 export const MAX_VOXELS = 10 ** 7;
@@ -90,6 +91,7 @@ export namespace VolsegEntryParamValues {
 
 export class VolsegEntry extends PluginStateObject.CreateBehavior<VolsegEntryData>({ name: 'Vol & Seg Entry' }) { }
 
+type VolRepr3DT = typeof StateTransforms.Representation.VolumeRepresentation3D
 
 export class VolsegEntryData extends PluginBehavior.WithSubscribers<VolsegEntryParamValues> {
     plugin: PluginContext;
@@ -111,6 +113,7 @@ export class VolsegEntryData extends PluginBehavior.WithSubscribers<VolsegEntryP
 
     private getStateNode = lazyGetter(() => this.plugin.state.data.selectQ(q => q.byRef(this.ref).subtree().ofType(VolsegState))[0] as StateObjectCell<VolsegState, StateTransform<typeof VolsegStateFromEntry>>, 'Missing VolsegState node. Must first create VolsegState for this VolsegEntry.');
     public currentState = new BehaviorSubject(ParamDefinition.getDefaultValues(VolsegStateParams));
+    public currentVolume = new BehaviorSubject<StateTransform<VolRepr3DT> | undefined>(undefined);
 
 
     private constructor(plugin: PluginContext, params: VolsegEntryParamValues) {
@@ -158,6 +161,14 @@ export class VolsegEntryData extends PluginBehavior.WithSubscribers<VolsegEntryP
             }
         });
 
+        this.subscribeObservable(this.plugin.state.data.events.cell.stateUpdated, e => {
+            // TODO: subscribe cell.removed event as well to set the current volume to undefined
+            if (e.cell.transform.tags?.includes(VOLUME_VISUAL_TAG)) {
+                // TODO: make sure this belongs to the "current entry subtree"
+                this.currentVolume.next(e.cell.transform);
+            }
+        });
+
         this.subscribeObservable(this.plugin.behaviors.interaction.click, async e => {
             const loci = e.current.loci;
             const clickedSegment = this.getSegmentIdFromLoci(loci);

+ 1 - 1
src/extensions/volumes-and-segmentations/entry-volume.ts

@@ -24,7 +24,7 @@ import { VolsegGlobalStateData } from './global-state';
 
 
 const GROUP_TAG = 'volume-group';
-const VOLUME_VISUAL_TAG = 'volume-visual';
+export const VOLUME_VISUAL_TAG = 'volume-visual';
 
 const DIRECT_VOLUME_RELATIVE_PEAK_HALFWIDTH = 0.5;
 

+ 19 - 9
src/extensions/volumes-and-segmentations/ui.tsx

@@ -12,6 +12,7 @@ import * as Icons from '../../mol-plugin-ui/controls/icons';
 import { ParameterControls } from '../../mol-plugin-ui/controls/parameters';
 import { Slider } from '../../mol-plugin-ui/controls/slider';
 import { useBehavior } from '../../mol-plugin-ui/hooks/use-behavior';
+import { UpdateTransformControl } from '../../mol-plugin-ui/state/update-transform';
 import { PluginContext } from '../../mol-plugin/context';
 import { shallowEqualArrays } from '../../mol-util';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
@@ -117,11 +118,6 @@ function VolsegEntryControls({ entryData }: { entryData: VolsegEntryData }) {
     const visibleModels = state.visibleModels.map(model => model.pdbId);
     const allPdbs = entryData.pdbs;
 
-    const volumeValues: SimpleVolumeParamValues = {
-        volumeType: state.volumeType,
-        opacity: state.volumeOpacity,
-    };
-
     return <>
         {/* Title */}
         <div style={{ fontWeight: 'bold', padding: 8, paddingTop: 6, paddingBottom: 4, overflow: 'hidden' }}>
@@ -139,10 +135,7 @@ function VolsegEntryControls({ entryData }: { entryData: VolsegEntryData }) {
         </ExpandGroup>}
 
         {/* Volume */}
-        <ExpandGroup header='Volume data' initiallyExpanded>
-            <WaitingParameterControls params={SimpleVolumeParams} values={volumeValues} onChangeValues={async next => { await sleep(20); await entryData.actionUpdateVolumeVisual(next); }} />
-        </ExpandGroup>
-
+        <VolumeControls entryData={entryData} />
         <ExpandGroup header='Segmentation data' initiallyExpanded>
             {/* Segment opacity slider */}
             <ControlRow label='Opacity' control={
@@ -189,6 +182,23 @@ function VolsegEntryControls({ entryData }: { entryData: VolsegEntryData }) {
     </>;
 }
 
+function VolumeControls({ entryData }: { entryData: VolsegEntryData }) {
+    const vol = useBehavior(entryData.currentVolume);
+    if (!vol) return null;
+
+    const volumeValues: SimpleVolumeParamValues = {
+        volumeType: vol.state.isHidden ? 'off' : vol.params?.type.name as any,
+        opacity: vol.params?.type.params.alpha,
+    };
+
+    return <ExpandGroup header='Volume data' initiallyExpanded>
+        <WaitingParameterControls params={SimpleVolumeParams} values={volumeValues} onChangeValues={async next => { await sleep(20); await entryData.actionUpdateVolumeVisual(next); }} />
+        <ExpandGroup header='Detailed Volume Params' headerStyle={{ marginTop: 1 }}>
+            <UpdateTransformControl state={entryData.plugin.state.data} transform={vol} customHeader='none' />
+        </ExpandGroup>
+    </ExpandGroup>;
+}
+
 type ComponentParams<T extends React.Component<any, any, any> | ((props: any) => JSX.Element)> =
     T extends React.Component<infer P, any, any> ? P : T extends (props: infer P) => JSX.Element ? P : never;