diff --git a/CHANGELOG.md b/CHANGELOG.md index 333e20650b749041eb54b70e2dc7bd244094fa1f..f79cc9af2b78fa58d38c13fd288b3b3b1fdd68f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Note that since we don't clearly distinguish between a public and private interf - Fix ``xrayShader`` & ``ignoreLight`` params not working at the same time - Add ``ignoreLight`` to component params - Fix representation preset side effects (changing post-processing parameters, see #363) +- Add Quick Styles panel (reset, illustrative, stylized) ## [v3.0.2] - 2022-01-30 diff --git a/src/mol-plugin-state/builder/structure/representation.ts b/src/mol-plugin-state/builder/structure/representation.ts index ace4803175e704e1eac272e459ce951d6bf0578d..fc2b05c029131622f3093d7c8de70b87e341cd2e 100644 --- a/src/mol-plugin-state/builder/structure/representation.ts +++ b/src/mol-plugin-state/builder/structure/representation.ts @@ -29,7 +29,7 @@ export class StructureRepresentationBuilder { readonly defaultProvider = PresetStructureRepresentations.auto; - private resolveProvider(ref: StructureRepresentationPresetProviderRef) { + resolveProvider(ref: StructureRepresentationPresetProviderRef) { return typeof ref === 'string' ? PresetStructureRepresentations[ref as keyof PresetStructureRepresentations] ?? arrayFind(this._providers, p => p.id === ref) : ref; diff --git a/src/mol-plugin-ui/controls.tsx b/src/mol-plugin-ui/controls.tsx index 5aaaaac424cf43dbaa1f8916cd37375a8f5751ac..7b2ffd0e13130e235e5b76a7faf73f1208d0cf45 100644 --- a/src/mol-plugin-ui/controls.tsx +++ b/src/mol-plugin-ui/controls.tsx @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> * @author Alexander Rose <alexander.rose@weirdbyte.de> @@ -24,6 +24,7 @@ import { StructureSourceControls } from './structure/source'; import { VolumeStreamingControls, VolumeSourceControls } from './structure/volume'; import { PluginConfig } from '../mol-plugin/config'; import { StructureSuperpositionControls } from './structure/superposition'; +import { StructureQuickStylesControls } from './structure/quick-styles'; export class TrajectoryViewportControls extends PluginUIComponent<{}, { show: boolean, label: string }> { state = { show: false, label: '' }; @@ -292,6 +293,7 @@ export class DefaultStructureTools extends PluginUIComponent { <StructureSourceControls /> <StructureMeasurementsControls /> <StructureSuperpositionControls /> + <StructureQuickStylesControls /> <StructureComponentControls /> {this.plugin.config.get(PluginConfig.VolumeStreaming.Enabled) && <VolumeStreamingControls />} <VolumeSourceControls /> diff --git a/src/mol-plugin-ui/controls/icons.tsx b/src/mol-plugin-ui/controls/icons.tsx index e069c42e3222f1378116c54a20aa7246904248c2..d4b9fb3948e6a5ca28b04788ff5dc0a05709abc9 100644 --- a/src/mol-plugin-ui/controls/icons.tsx +++ b/src/mol-plugin-ui/controls/icons.tsx @@ -1,5 +1,5 @@ /** - * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> * @author Alexander Rose <alexander.rose@weirdbyte.de> @@ -38,10 +38,10 @@ export function MoleculeSvg() { return _Molecule; } // The following icons are adapted from https://materialdesignicons.com/ and // licensed with https://github.com/Templarian/MaterialDesign/blob/master/LICENSE -const _CubeOutline = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d="M21,16.5C21,16.88 20.79,17.21 20.47,17.38L12.57,21.82C12.41,21.94 12.21,22 12,22C11.79,22 11.59,21.94 11.43,21.82L3.53,17.38C3.21,17.21 3,16.88 3,16.5V7.5C3,7.12 3.21,6.79 3.53,6.62L11.43,2.18C11.59,2.06 11.79,2 12,2C12.21,2 12.41,2.06 12.57,2.18L20.47,6.62C20.79,6.79 21,7.12 21,7.5V16.5M12,4.15L6.04,7.5L12,10.85L17.96,7.5L12,4.15M5,15.91L11,19.29V12.58L5,9.21V15.91M19,15.91V9.21L13,12.58V19.29L19,15.91Z" /></svg>; +const _CubeOutline = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d='M21,16.5C21,16.88 20.79,17.21 20.47,17.38L12.57,21.82C12.41,21.94 12.21,22 12,22C11.79,22 11.59,21.94 11.43,21.82L3.53,17.38C3.21,17.21 3,16.88 3,16.5V7.5C3,7.12 3.21,6.79 3.53,6.62L11.43,2.18C11.59,2.06 11.79,2 12,2C12.21,2 12.41,2.06 12.57,2.18L20.47,6.62C20.79,6.79 21,7.12 21,7.5V16.5M12,4.15L6.04,7.5L12,10.85L17.96,7.5L12,4.15M5,15.91L11,19.29V12.58L5,9.21V15.91M19,15.91V9.21L13,12.58V19.29L19,15.91Z' /></svg>; export function CubeOutlineSvg() { return _CubeOutline; } -const _CubeScan = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d="M17,22V20H20V17H22V20.5C22,20.89 21.84,21.24 21.54,21.54C21.24,21.84 20.89,22 20.5,22H17M7,22H3.5C3.11,22 2.76,21.84 2.46,21.54C2.16,21.24 2,20.89 2,20.5V17H4V20H7V22M17,2H20.5C20.89,2 21.24,2.16 21.54,2.46C21.84,2.76 22,3.11 22,3.5V7H20V4H17V2M7,2V4H4V7H2V3.5C2,3.11 2.16,2.76 2.46,2.46C2.76,2.16 3.11,2 3.5,2H7M13,17.25L17,14.95V10.36L13,12.66V17.25M12,10.92L16,8.63L12,6.28L8,8.63L12,10.92M7,14.95L11,17.25V12.66L7,10.36V14.95M18.23,7.59C18.73,7.91 19,8.34 19,8.91V15.23C19,15.8 18.73,16.23 18.23,16.55L12.75,19.73C12.25,20.05 11.75,20.05 11.25,19.73L5.77,16.55C5.27,16.23 5,15.8 5,15.23V8.91C5,8.34 5.27,7.91 5.77,7.59L11.25,4.41C11.5,4.28 11.75,4.22 12,4.22C12.25,4.22 12.5,4.28 12.75,4.41L18.23,7.59Z" /></svg>; +const _CubeScan = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d='M17,22V20H20V17H22V20.5C22,20.89 21.84,21.24 21.54,21.54C21.24,21.84 20.89,22 20.5,22H17M7,22H3.5C3.11,22 2.76,21.84 2.46,21.54C2.16,21.24 2,20.89 2,20.5V17H4V20H7V22M17,2H20.5C20.89,2 21.24,2.16 21.54,2.46C21.84,2.76 22,3.11 22,3.5V7H20V4H17V2M7,2V4H4V7H2V3.5C2,3.11 2.16,2.76 2.46,2.46C2.76,2.16 3.11,2 3.5,2H7M13,17.25L17,14.95V10.36L13,12.66V17.25M12,10.92L16,8.63L12,6.28L8,8.63L12,10.92M7,14.95L11,17.25V12.66L7,10.36V14.95M18.23,7.59C18.73,7.91 19,8.34 19,8.91V15.23C19,15.8 18.73,16.23 18.23,16.55L12.75,19.73C12.25,20.05 11.75,20.05 11.25,19.73L5.77,16.55C5.27,16.23 5,15.8 5,15.23V8.91C5,8.34 5.27,7.91 5.77,7.59L11.25,4.41C11.5,4.28 11.75,4.22 12,4.22C12.25,4.22 12.5,4.28 12.75,4.41L18.23,7.59Z' /></svg>; export function CubeScanSvg() { return _CubeScan; } const _CubeSend = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d="M16,4L9,8.04V15.96L16,20L23,15.96V8.04M16,6.31L19.8,8.5L16,10.69L12.21,8.5M0,7V9H7V7M11,10.11L15,12.42V17.11L11,14.81M21,10.11V14.81L17,17.11V12.42M2,11V13H7V11M4,15V17H7V15" /></svg>; @@ -53,9 +53,12 @@ export function CursorDefaultOutlineSvg() { return _CursorDefaultOutline; } const _FileOutline = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path fill='currentColor' d='M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z' /></svg>; export function FileOutlineSvg() { return _FileOutline; } -const _PencilRuler = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d="M3 17.25V21H6.75L17.81 9.93L14.06 6.18L3 17.25M22.61 18.36L18.36 22.61L13.16 17.41L14.93 15.64L15.93 16.64L18.4 14.16L19.82 15.58L18.36 17L19.42 18L20.84 16.6L22.61 18.36M6.61 10.83L1.39 5.64L5.64 1.39L7.4 3.16L4.93 5.64L6 6.7L8.46 4.22L9.88 5.64L8.46 7.05L9.46 8.05L6.61 10.83M20.71 7C21.1 6.61 21.1 6 20.71 5.59L18.37 3.29C18 2.9 17.35 2.9 16.96 3.29L15.12 5.12L18.87 8.87L20.71 7Z" /></svg>; +const _PencilRuler = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d='M3 17.25V21H6.75L17.81 9.93L14.06 6.18L3 17.25M22.61 18.36L18.36 22.61L13.16 17.41L14.93 15.64L15.93 16.64L18.4 14.16L19.82 15.58L18.36 17L19.42 18L20.84 16.6L22.61 18.36M6.61 10.83L1.39 5.64L5.64 1.39L7.4 3.16L4.93 5.64L6 6.7L8.46 4.22L9.88 5.64L8.46 7.05L9.46 8.05L6.61 10.83M20.71 7C21.1 6.61 21.1 6 20.71 5.59L18.37 3.29C18 2.9 17.35 2.9 16.96 3.29L15.12 5.12L18.87 8.87L20.71 7Z' /></svg>; export function PencilRulerSvg() { return _PencilRuler; } +const _MagicWand = <svg width='24px' height='24px' viewBox='0 0 24 24'><path fill='currentColor' d='M7.5,5.6L5,7L6.4,4.5L5,2L7.5,3.4L10,2L8.6,4.5L10,7L7.5,5.6M19.5,15.4L22,14L20.6,16.5L22,19L19.5,17.6L17,19L18.4,16.5L17,14L19.5,15.4M22,2L20.6,4.5L22,7L19.5,5.6L17,7L18.4,4.5L17,2L19.5,3.4L22,2M13.34,12.78L15.78,10.34L13.66,8.22L11.22,10.66L13.34,12.78M14.37,7.29L16.71,9.63C17.1,10 17.1,10.65 16.71,11.04L5.04,22.71C4.65,23.1 4,23.1 3.63,22.71L1.29,20.37C0.9,20 0.9,19.35 1.29,18.96L12.96,7.29C13.35,6.9 14,6.9 14.37,7.29Z' /></svg>; +export function MagicWandSvg() { return _MagicWand; } + // The following icons are adapted from https://material-ui.com/components/material-icons/ and // licensed with https://github.com/mui-org/material-ui/blob/master/LICENSE diff --git a/src/mol-plugin-ui/structure/quick-styles.tsx b/src/mol-plugin-ui/structure/quick-styles.tsx new file mode 100644 index 0000000000000000000000000000000000000000..bf1b39c615aedfc67b7fda7cc279aee3f1e71cf0 --- /dev/null +++ b/src/mol-plugin-ui/structure/quick-styles.tsx @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { PresetStructureRepresentations } from '../../mol-plugin-state/builder/structure/representation-preset'; +import { Color } from '../../mol-util/color'; +import { CollapsableControls, PurePluginUIComponent } from '../base'; +import { Button } from '../controls/common'; +import { MagicWandSvg } from '../controls/icons'; +import { ParamDefinition as PD } from '../../mol-util/param-definition'; +import { PostprocessingParams } from '../../mol-canvas3d/passes/postprocessing'; +import { PluginConfig } from '../../mol-plugin/config'; +import { StructureComponentManager } from '../../mol-plugin-state/manager/structure/component'; + +export class StructureQuickStylesControls extends CollapsableControls { + defaultState() { + return { + isCollapsed: false, + header: 'Quick Styles', + brand: { accent: 'gray' as const, svg: MagicWandSvg } + }; + } + + renderControls() { + return <> + <QuickStyles /> + </>; + } +} + +export class QuickStyles extends PurePluginUIComponent { + async reset() { + const { structures } = this.plugin.managers.structure.hierarchy.selection; + const preset = this.plugin.config.get(PluginConfig.Structure.DefaultRepresentationPreset) || PresetStructureRepresentations.auto.id; + const provider = this.plugin.builders.structure.representation.resolveProvider(preset); + await this.plugin.managers.structure.component.applyPreset(structures, provider); + + this.plugin.managers.structure.component.setOptions(PD.getDefaultValues(StructureComponentManager.OptionsParams)); + + if (this.plugin.canvas3d) { + const p = PD.getDefaultValues(PostprocessingParams); + this.plugin.canvas3d.setProps({ + postprocessing: { outline: p.outline, occlusion: p.occlusion } + }); + } + } + + async illustrative() { + const { structures } = this.plugin.managers.structure.hierarchy.selection; + await this.plugin.managers.structure.component.applyPreset(structures, PresetStructureRepresentations.illustrative); + + if (this.plugin.canvas3d) { + this.plugin.canvas3d.setProps({ + postprocessing: { + outline: { + name: 'on', + params: { scale: 1, color: Color(0x000000), threshold: 0.25 } + }, + occlusion: { + name: 'on', + params: { bias: 0.9, blurKernelSize: 15, radius: 5, samples: 32 } + }, + } + }); + } + } + + async stylized() { + this.plugin.managers.structure.component.setOptions({ ...this.plugin.managers.structure.component.state.options, ignoreLight: true }); + + if (this.plugin.canvas3d) { + const pp = this.plugin.canvas3d.props.postprocessing; + this.plugin.canvas3d.setProps({ + postprocessing: { + outline: { + name: 'on', + params: pp.outline.name === 'on' + ? pp.outline.params + : { scale: 1, color: Color(0x000000), threshold: 0.33 } + }, + occlusion: { + name: 'on', + params: pp.occlusion.name === 'on' + ? pp.occlusion.params + : { bias: 0.9, blurKernelSize: 15, radius: 5, samples: 32 } + }, + } + }); + } + } + + render() { + return <div className='msp-flex-row'> + <Button noOverflow title='Applies default representation preset. Set outline and occlusion effects to defaults.' onClick={() => this.reset()} style={{ width: 'auto' }}> + Reset + </Button> + <Button noOverflow title='Applies illustrative representation preset. Enables outline and occlusion effects. Enables ignore-light parameter.' onClick={() => this.illustrative()} style={{ width: 'auto' }}> + Illustrative + </Button> + <Button noOverflow title='Applies no representation preset. Enables outline and occlusion effects. Enables ignore-light representation parameter.' onClick={() => this.stylized()} style={{ width: 'auto' }}> + Stylized + </Button> + </div>; + } +} \ No newline at end of file