diff --git a/CHANGELOG.md b/CHANGELOG.md index 82f75dd1023776b47d940f4c87294ff8ddb71e21..fb0728b21d75b23eecf74c0f4ca014163119e8eb 100644 --- a/CHANGELOG.md +++ b/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 diff --git a/src/extensions/volumes-and-segmentations/entry-root.ts b/src/extensions/volumes-and-segmentations/entry-root.ts index 76971ac66c9d192b3a70cd7d70662d460681a379..0918145b15ec082f7aac23a4c0308ab3a6aa8ca4 100644 --- a/src/extensions/volumes-and-segmentations/entry-root.ts +++ b/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); diff --git a/src/extensions/volumes-and-segmentations/entry-volume.ts b/src/extensions/volumes-and-segmentations/entry-volume.ts index 1ba4184ac9c9b1e0e37c37f74ed3e4fa952feb68..930edc5ccafade79ffa5c136930d37bd379912aa 100644 --- a/src/extensions/volumes-and-segmentations/entry-volume.ts +++ b/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; diff --git a/src/extensions/volumes-and-segmentations/ui.tsx b/src/extensions/volumes-and-segmentations/ui.tsx index 65df31a57e310b9efb12e62fd1ea5cad66d93210..0d8a24cb08759a6950ced3b9992c4f0e08e3938d 100644 --- a/src/extensions/volumes-and-segmentations/ui.tsx +++ b/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;