소스 검색

Volume Quick Color Select Control (#677)

* changelog

* volume quick select control

* tweak
David Sehnal 2 년 전
부모
커밋
4c03009357
3개의 변경된 파일64개의 추가작업 그리고 17개의 파일을 삭제
  1. 3 1
      CHANGELOG.md
  2. 21 12
      src/mol-plugin-ui/controls/color.tsx
  3. 40 4
      src/mol-plugin-ui/structure/volume.tsx

+ 3 - 1
CHANGELOG.md

@@ -6,7 +6,8 @@ Note that since we don't clearly distinguish between a public and private interf
 
 ## [Unreleased]
 
-- Fixed a bug in mesh visualization (show backfaces when opacity < 1)
+- `meshes` extension: Fixed a bug in mesh visualization (show backfaces when opacity < 1) 
+- Add color quick select control to Volume controls
 
 ## [v3.28.0] - 2022-12-20
 
@@ -14,6 +15,7 @@ Note that since we don't clearly distinguish between a public and private interf
 - Add `solidInterior` parameter to sphere/cylinder impostors
 - [Breaking] Tweak `ignoreHydrogens` non-polar handling (introduced in 3.27.0)
 - Add `meshes` and `volumes-and-segmentations` extensions
+    - See https://molstarvolseg.ncbr.muni.cz/ for more info
 - Fix missing support for info in `ParamDefinition.Converted`
 - Add support for multi-visual volume representations
 - Improve volume isosurface bounding-sphere

+ 21 - 12
src/mol-plugin-ui/controls/color.tsx

@@ -14,9 +14,9 @@ import { ParamProps } from './parameters';
 import { TextInput, Button, ControlRow } from './common';
 import { DefaultColorSwatch } from '../../mol-util/color/swatches';
 
-export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Color>, { isExpanded: boolean, lightness: number }> {
+export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Color> & { hideNameRow?: boolean }, { isExpanded: boolean, lightness: number }> {
     state = {
-        isExpanded: !!this.props.param.isExpanded,
+        isExpanded: !!this.props.param.isExpanded || !!this.props.hideNameRow,
         lightness: 0
     };
 
@@ -72,21 +72,30 @@ export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Colo
     render() {
         const label = this.props.param.label || camelCaseToWords(this.props.name);
         const [r, g, b] = Color.toRgb(this.props.value);
+
+        const inner = <>
+            {this.swatch()}
+            <ControlRow label='RGB' className='msp-control-label-short' control={<div style={{ display: 'flex', textAlignLast: 'center', left: '80px' }}>
+                <TextInput onChange={this.onR} numeric value={r} delayMs={250} style={{ order: 1, flex: '1 1 auto', minWidth: 0 }} className='msp-form-control' onEnter={this.props.onEnter} blurOnEnter={true} blurOnEscape={true} />
+                <TextInput onChange={this.onG} numeric value={g} delayMs={250} style={{ order: 2, flex: '1 1 auto', minWidth: 0 }} className='msp-form-control' onEnter={this.props.onEnter} blurOnEnter={true} blurOnEscape={true} />
+                <TextInput onChange={this.onB} numeric value={b} delayMs={250} style={{ order: 3, flex: '1 1 auto', minWidth: 0 }} className='msp-form-control' onEnter={this.props.onEnter} blurOnEnter={true} blurOnEscape={true} />
+            </div>} />
+            <div style={{ display: 'flex', textAlignLast: 'center' }}>
+                <Button onClick={this.onLighten} style={{ order: 1, flex: '1 1 auto', minWidth: 0 }} className='msp-form-control'>Lighten</Button>
+                <Button onClick={this.onDarken} style={{ order: 1, flex: '1 1 auto', minWidth: 0 }} className='msp-form-control'>Darken</Button>
+            </div>
+        </>;
+
+        if (this.props.hideNameRow) {
+            return inner;
+        }
+
         return <>
             <ControlRow title={this.props.param.description}
                 label={label}
                 control={<Button onClick={this.toggleExpanded} inline className='msp-combined-color-button' style={{ background: Color.toStyle(this.props.value) }} />} />
             {this.state.isExpanded && <div className='msp-control-offset'>
-                {this.swatch()}
-                <ControlRow label='RGB' className='msp-control-label-short' control={<div style={{ display: 'flex', textAlignLast: 'center', left: '80px' }}>
-                    <TextInput onChange={this.onR} numeric value={r} delayMs={250} style={{ order: 1, flex: '1 1 auto', minWidth: 0 }} className='msp-form-control' onEnter={this.props.onEnter} blurOnEnter={true} blurOnEscape={true} />
-                    <TextInput onChange={this.onG} numeric value={g} delayMs={250} style={{ order: 2, flex: '1 1 auto', minWidth: 0 }} className='msp-form-control' onEnter={this.props.onEnter} blurOnEnter={true} blurOnEscape={true} />
-                    <TextInput onChange={this.onB} numeric value={b} delayMs={250} style={{ order: 3, flex: '1 1 auto', minWidth: 0 }} className='msp-form-control' onEnter={this.props.onEnter} blurOnEnter={true} blurOnEscape={true} />
-                </div>}/>
-                <div style={{ display: 'flex', textAlignLast: 'center' }}>
-                    <Button onClick={this.onLighten} style={{ order: 1, flex: '1 1 auto', minWidth: 0 }} className='msp-form-control'>Lighten</Button>
-                    <Button onClick={this.onDarken} style={{ order: 1, flex: '1 1 auto', minWidth: 0 }} className='msp-form-control'>Darken</Button>
-                </div>
+                {inner}
             </div>}
         </>;
     }

+ 40 - 4
src/mol-plugin-ui/structure/volume.tsx

@@ -15,15 +15,19 @@ import { InitVolumeStreaming } from '../../mol-plugin/behavior/dynamic/volume-st
 import { State, StateObjectCell, StateObjectSelector, StateSelection, StateTransform } from '../../mol-state';
 import { CollapsableControls, CollapsableState, PurePluginUIComponent } from '../base';
 import { ActionMenu } from '../controls/action-menu';
-import { Button, ExpandGroup, IconButton } from '../controls/common';
+import { Button, ControlGroup, ExpandGroup, IconButton } from '../controls/common';
 import { ApplyActionControl } from '../state/apply-action';
 import { UpdateTransformControl } from '../state/update-transform';
 import { BindingsHelp } from '../viewport/help';
 import { PluginCommands } from '../../mol-plugin/commands';
-import { BlurOnSvg, ErrorSvg, CheckSvg, AddSvg, VisibilityOffOutlinedSvg, VisibilityOutlinedSvg, DeleteOutlinedSvg, MoreHorizSvg } from '../controls/icons';
+import { BlurOnSvg, ErrorSvg, CheckSvg, AddSvg, VisibilityOffOutlinedSvg, VisibilityOutlinedSvg, DeleteOutlinedSvg, MoreHorizSvg, CloseSvg } from '../controls/icons';
 import { PluginStateObject } from '../../mol-plugin-state/objects';
 import { StateTransforms } from '../../mol-plugin-state/transforms';
 import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers/volume-representation-params';
+import { Color } from '../../mol-util/color';
+import { ParamDefinition } from '../../mol-util/param-definition';
+import { CombinedColorControl } from '../controls/color';
+import { ParamOnChange } from '../controls/parameters';
 
 interface VolumeStreamingControlState extends CollapsableState {
     isBusy: boolean
@@ -260,7 +264,7 @@ export class VolumeSourceControls extends CollapsableControls<{}, VolumeSourceCo
     }
 }
 
-type VolumeRepresentationEntryActions = 'update'
+type VolumeRepresentationEntryActions = 'update' | 'select-color'
 
 class VolumeRepresentationControls extends PurePluginUIComponent<{ representation: VolumeRepresentationRef }, { action?: VolumeRepresentationEntryActions }> {
     state = { action: void 0 as VolumeRepresentationEntryActions | undefined };
@@ -279,6 +283,10 @@ class VolumeRepresentationControls extends PurePluginUIComponent<{ representatio
         this.plugin.managers.volume.hierarchy.toggleVisibility([this.props.representation]);
     };
 
+    toggleColor = () => {
+        this.setState({ action: this.state.action === 'select-color' ? undefined : 'select-color' });
+    };
+
     toggleUpdate = () => this.setState({ action: this.state.action === 'update' ? void 0 : 'update' });
 
     highlight = (e: React.MouseEvent<HTMLElement>) => {
@@ -299,10 +307,30 @@ class VolumeRepresentationControls extends PurePluginUIComponent<{ representatio
         if (lociList) this.plugin.managers.camera.focusLoci(lociList, { extraRadius: 1 });
     };
 
+    private get color() {
+        const repr = this.props.representation.cell;
+        const isUniform = repr.transform.params?.colorTheme.name === 'uniform';
+        if (!isUniform) return void 0;
+        return repr.transform.params?.colorTheme.params.value;
+    }
+
+    updateColor: ParamOnChange = ({ value }) => {
+        const t = this.props.representation.cell.transform;
+        return this.plugin.build().to(t.ref).update({
+            ...t.params,
+            colorTheme: {
+                name: 'uniform',
+                params: { value }
+            },
+        }).commit();
+    };
+
     render() {
         const repr = this.props.representation.cell;
+        const color = this.color;
         return <>
             <div className='msp-flex-row'>
+                {color !== void 0 && <Button style={{ backgroundColor: Color.toStyle(color), minWidth: 32, width: 32 }} onClick={this.toggleColor} />}
                 <Button noOverflow className='msp-control-button-label' title={`${repr.obj?.label}. Click to focus.`} onClick={this.focus} onMouseEnter={this.highlight} onMouseLeave={this.clearHighlight} style={{ textAlign: 'left' }}>
                     {repr.obj?.label}
                     <small className='msp-25-lower-contrast-text' style={{ float: 'right' }}>{repr.obj?.description}</small>
@@ -314,6 +342,14 @@ class VolumeRepresentationControls extends PurePluginUIComponent<{ representatio
             {this.state.action === 'update' && !!repr.parent && <div style={{ marginBottom: '6px' }} className='msp-accent-offset'>
                 <UpdateTransformControl state={repr.parent} transform={repr.transform} customHeader='none' noMargin />
             </div>}
+            {this.state.action === 'select-color' && color !== void 0 && <div style={{ marginBottom: '6px', marginTop: 1 }} className='msp-accent-offset'>
+                <ControlGroup header='Select Color' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleColor}
+                    topRightIcon={CloseSvg} noTopMargin childrenClassName='msp-viewport-controls-panel-controls'>
+                    <CombinedColorControl param={VolumeColorParam} value={this.color} onChange={this.updateColor} name='color' hideNameRow />
+                </ControlGroup>
+            </div>}
         </>;
     }
-}
+}
+
+const VolumeColorParam = ParamDefinition.Color(Color(0x121212));