simple-settings.tsx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. /**
  2. * Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. * @author David Sehnal <david.sehnal@gmail.com>
  6. */
  7. import { produce } from 'immer';
  8. import { throttleTime } from 'rxjs';
  9. import { Canvas3DParams, Canvas3DProps } from '../../mol-canvas3d/canvas3d';
  10. import { PluginCommands } from '../../mol-plugin/commands';
  11. import { PluginConfig } from '../../mol-plugin/config';
  12. import { StateTransform } from '../../mol-state';
  13. import { Color } from '../../mol-util/color';
  14. import { deepClone } from '../../mol-util/object';
  15. import { ParamDefinition as PD } from '../../mol-util/param-definition';
  16. import { ParamMapping } from '../../mol-util/param-mapping';
  17. import { Mutable } from '../../mol-util/type-helpers';
  18. import { PluginUIComponent } from '../base';
  19. import { PluginUIContext } from '../context';
  20. import { ParameterMappingControl } from '../controls/parameters';
  21. import { ViewportHelpContent } from './help';
  22. export class SimpleSettingsControl extends PluginUIComponent {
  23. componentDidMount() {
  24. if (!this.plugin.canvas3d) return;
  25. this.subscribe(this.plugin.events.canvas3d.settingsUpdated, () => this.forceUpdate());
  26. this.subscribe(this.plugin.canvas3d!.camera.stateChanged.pipe(throttleTime(500, undefined, { leading: true, trailing: true })), state => {
  27. if (state.radiusMax !== undefined || state.radius !== undefined) {
  28. this.forceUpdate();
  29. }
  30. });
  31. }
  32. render() {
  33. if (!this.plugin.canvas3d) return null;
  34. return <>
  35. <ParameterMappingControl mapping={SimpleSettingsMapping} />
  36. <ViewportHelpContent />
  37. </>;
  38. }
  39. }
  40. const LayoutOptions = {
  41. 'sequence': 'Sequence',
  42. 'log': 'Log',
  43. 'left': 'Left Panel',
  44. 'right': 'Right Panel',
  45. };
  46. type LayoutOptions = keyof typeof LayoutOptions
  47. const SimpleSettingsParams = {
  48. animate: Canvas3DParams.trackball.params.animate,
  49. camera: Canvas3DParams.camera,
  50. background: PD.Group({
  51. color: PD.Color(Color(0xFCFBF9), { label: 'Background', description: 'Custom background color' }),
  52. transparent: PD.Boolean(false),
  53. style: Canvas3DParams.postprocessing.params.background,
  54. }, { pivot: 'color' }),
  55. lighting: PD.Group({
  56. occlusion: Canvas3DParams.postprocessing.params.occlusion,
  57. shadow: Canvas3DParams.postprocessing.params.shadow,
  58. outline: Canvas3DParams.postprocessing.params.outline,
  59. fog: Canvas3DParams.cameraFog,
  60. }, { isFlat: true }),
  61. clipping: PD.Group<any>({
  62. ...Canvas3DParams.cameraClipping.params,
  63. }, { pivot: 'radius' }),
  64. layout: PD.MultiSelect([] as LayoutOptions[], PD.objectToOptions(LayoutOptions)),
  65. };
  66. type SimpleSettingsParams = typeof SimpleSettingsParams
  67. const SimpleSettingsMapping = ParamMapping({
  68. params: (ctx: PluginUIContext) => {
  69. const params = PD.clone(SimpleSettingsParams);
  70. const controls = ctx.spec.components?.controls;
  71. if (controls) {
  72. const options: [LayoutOptions, string][] = [];
  73. if (controls.top !== 'none') options.push(['sequence', LayoutOptions.sequence]);
  74. if (controls.bottom !== 'none') options.push(['log', LayoutOptions.log]);
  75. if (controls.left !== 'none') options.push(['left', LayoutOptions.left]);
  76. if (controls.right !== 'none') options.push(['right', LayoutOptions.right]);
  77. params.layout.options = options;
  78. }
  79. const bgStyles = ctx.config.get(PluginConfig.Background.Styles) || [];
  80. if (bgStyles.length > 0) {
  81. Object.assign(params.background.params.style, {
  82. presets: deepClone(bgStyles),
  83. isFlat: false, // so the presets menu is shown
  84. });
  85. }
  86. return params;
  87. },
  88. target(ctx: PluginUIContext) {
  89. const c = ctx.spec.components?.controls;
  90. const r = ctx.layout.state.regionState;
  91. const layout: SimpleSettingsParams['layout']['defaultValue'] = [];
  92. if (r.top !== 'hidden' && (!c || c.top !== 'none')) layout.push('sequence');
  93. if (r.bottom !== 'hidden' && (!c || c.bottom !== 'none')) layout.push('log');
  94. if (r.left !== 'hidden' && (!c || c.left !== 'none')) layout.push('left');
  95. if (r.right !== 'hidden' && (!c || c.right !== 'none')) layout.push('right');
  96. return { canvas: ctx.canvas3d?.props!, layout };
  97. }
  98. })({
  99. values(props, ctx) {
  100. const { canvas } = props;
  101. const renderer = canvas.renderer;
  102. return {
  103. layout: props.layout,
  104. animate: canvas.trackball.animate,
  105. camera: canvas.camera,
  106. background: {
  107. color: renderer.backgroundColor,
  108. transparent: canvas.transparentBackground,
  109. style: canvas.postprocessing.background,
  110. },
  111. lighting: {
  112. occlusion: canvas.postprocessing.occlusion,
  113. shadow: canvas.postprocessing.shadow,
  114. outline: canvas.postprocessing.outline,
  115. fog: canvas.cameraFog,
  116. },
  117. clipping: {
  118. ...canvas.cameraClipping,
  119. }
  120. };
  121. },
  122. update(s, props) {
  123. const canvas = props.canvas as Mutable<Canvas3DProps>;
  124. canvas.trackball.animate = s.animate;
  125. canvas.camera = s.camera;
  126. canvas.transparentBackground = s.background.transparent;
  127. canvas.renderer.backgroundColor = s.background.color;
  128. canvas.postprocessing.occlusion = s.lighting.occlusion;
  129. canvas.postprocessing.shadow = s.lighting.shadow;
  130. canvas.postprocessing.outline = s.lighting.outline;
  131. canvas.postprocessing.background = s.background.style;
  132. canvas.cameraFog = s.lighting.fog;
  133. canvas.cameraClipping = {
  134. radius: s.clipping.radius,
  135. far: s.clipping.far,
  136. minNear: s.clipping.minNear,
  137. };
  138. props.layout = s.layout;
  139. },
  140. async apply(props, ctx) {
  141. await PluginCommands.Canvas3D.SetSettings(ctx, { settings: props.canvas });
  142. const hideLeft = props.layout.indexOf('left') < 0;
  143. const state = produce(ctx.layout.state, s => {
  144. s.regionState.top = props.layout.indexOf('sequence') >= 0 ? 'full' : 'hidden';
  145. s.regionState.bottom = props.layout.indexOf('log') >= 0 ? 'full' : 'hidden';
  146. s.regionState.left = hideLeft ? 'hidden' : ctx.behaviors.layout.leftPanelTabName.value === 'none' ? 'collapsed' : 'full';
  147. s.regionState.right = props.layout.indexOf('right') >= 0 ? 'full' : 'hidden';
  148. });
  149. await PluginCommands.Layout.Update(ctx, { state });
  150. if (hideLeft) {
  151. PluginCommands.State.SetCurrentObject(ctx, { state: ctx.state.data, ref: StateTransform.RootRef });
  152. }
  153. }
  154. });