objects.tsx 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /**
  2. * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import * as React from 'react';
  7. import { CollapsableControls, CollapsableState, PurePluginUIComponent } from '../base';
  8. import { StateTransformer, StateTransform, StateObjectCell } from '../../mol-state';
  9. import { ModelRef, GenericRepresentationRef } from '../../mol-plugin-state/manager/structure/hierarchy-state';
  10. import { StateTransforms } from '../../mol-plugin-state/transforms';
  11. import { StructureBuilderTags } from '../../mol-plugin-state/builder/structure';
  12. import { IconButton } from '../controls/common';
  13. import { UpdateTransformControl } from '../state/update-transform';
  14. import { PluginStateObject } from '../../mol-plugin-state/objects';
  15. import { PluginCommands } from '../../mol-plugin/commands';
  16. interface ObjectControlState extends CollapsableState {
  17. isBusy: boolean,
  18. showOptions: boolean,
  19. }
  20. export class ObjectControls extends CollapsableControls<{}, ObjectControlState> {
  21. protected defaultState(): ObjectControlState {
  22. return {
  23. header: 'Objects',
  24. isCollapsed: false,
  25. isBusy: false,
  26. showOptions: false
  27. };
  28. }
  29. componentDidMount() {
  30. this.subscribe(this.plugin.managers.structure.hierarchy.behaviors.selection, () => this.forceUpdate());
  31. this.subscribe(this.plugin.behaviors.state.isBusy, v => {
  32. this.setState({ isBusy: v })
  33. });
  34. }
  35. getUnitcell(model: ModelRef) {
  36. return model.genericRepresentations?.filter(r => {
  37. return r.cell.transform.transformer === StateTransforms.Representation.ModelUnitcell3D
  38. })[0]
  39. }
  40. toggleVisible = (e: React.MouseEvent<HTMLElement>) => {
  41. e.preventDefault();
  42. e.currentTarget.blur();
  43. for (const model of this.plugin.managers.structure.hierarchy.current.models) {
  44. const unitcell = this.getUnitcell(model)
  45. if (unitcell) {
  46. this.plugin.state.data.updateCellState(unitcell.cell.transform.ref, { isHidden: !unitcell.cell.state.isHidden });
  47. }
  48. }
  49. }
  50. isVisible() {
  51. for (const model of this.plugin.managers.structure.hierarchy.current.models) {
  52. const unitcell = this.getUnitcell(model)
  53. if (unitcell && !unitcell.cell.state.isHidden) return true
  54. }
  55. return false
  56. }
  57. async createUnitcell(model: ModelRef, params?: StateTransformer.Params<StateTransforms['Representation']['ModelUnitcell3D']>, initialState?: Partial<StateTransform.State>) {
  58. const state = this.plugin.state.data;
  59. const unitcell = state.build().to(model.cell)
  60. .apply(StateTransforms.Representation.ModelUnitcell3D, params, { tags: StructureBuilderTags.ModelGenericRepresentation, state: initialState });
  61. await this.plugin.updateDataState(unitcell, { revertOnError: true });
  62. return unitcell.selector;
  63. }
  64. ensureUnitcell = async () => {
  65. for (const model of this.plugin.managers.structure.hierarchy.current.models) {
  66. if (!this.getUnitcell(model)) await this.createUnitcell(model)
  67. }
  68. }
  69. highlight = (e: React.MouseEvent<HTMLElement>) => {
  70. e.preventDefault();
  71. // TODO
  72. // PluginCommands.Interactivity.Object.Highlight(this.plugin, { state: this.props.group[0].cell.parent, ref: this.props.group.map(c => c.cell.transform.ref) });
  73. }
  74. clearHighlight = (e: React.MouseEvent<HTMLElement>) => {
  75. e.preventDefault();
  76. // TODO
  77. // PluginCommands.Interactivity.ClearHighlights(this.plugin);
  78. }
  79. focus = () => {
  80. // TODO
  81. // const sphere = this.pivot.cell.obj?.data.boundary.sphere;
  82. // if (sphere) this.plugin.managers.camera.focusSphere(sphere);
  83. }
  84. toggleOptions = () => {
  85. // TODO
  86. this.setState({ showOptions: !this.state.showOptions })
  87. }
  88. renderUnitcell(unitcell: GenericRepresentationRef) {
  89. const label = 'Unitcell'
  90. const isVisible = unitcell.cell.state.isHidden
  91. return <>
  92. <div className='msp-control-row'>
  93. <button className='msp-control-button-label' title={`${label}. Click to focus.`} onClick={this.focus} onMouseEnter={this.highlight} onMouseLeave={this.clearHighlight} style={{ textAlign: 'left' }}>
  94. {label}
  95. </button>
  96. <div className='msp-select-row'>
  97. <IconButton onClick={this.toggleVisible} icon='visual-visibility' toggleState={isVisible} title={`${isVisible ? 'Hide' : 'Show'} component`} disabled={this.state.isBusy} style={{ flex: '0 0 40px' }} />
  98. <IconButton onClick={this.toggleOptions} icon='cog' title='Options' toggleState={this.state.showOptions} disabled={this.state.isBusy} style={{ flex: '0 0 40px' }} />
  99. </div>
  100. </div>
  101. {this.state.showOptions && unitcell && <>
  102. <div className='msp-control-offset'>
  103. <UpdateTransformControl state={unitcell.cell.parent} transform={unitcell.cell.transform} customHeader='none' />
  104. </div>
  105. </>}
  106. </>;
  107. }
  108. renderControls() {
  109. const objects: JSX.Element[] = [];
  110. for (const model of this.plugin.managers.structure.hierarchy.current.models) {
  111. const cell = this.getUnitcell(model)?.cell
  112. if (cell && cell.obj) objects.push(<UnitcellEntry key={cell.obj.id} cell={cell} />)
  113. }
  114. return <>
  115. <>{objects}</>
  116. <div className='msp-control-group-header msp-flex-row' style={{ marginTop: '1px' }}>
  117. <button className='msp-btn msp-form-control msp-flex-item msp-no-overflow' onClick={this.ensureUnitcell}>
  118. Unitcell
  119. </button>
  120. </div>
  121. </>;
  122. }
  123. }
  124. export type UnitcellCell = StateObjectCell<PluginStateObject.Shape.Representation3D, StateTransform<StateTransformer<PluginStateObject.Molecule.Structure.Selections, PluginStateObject.Shape.Representation3D, any>>>
  125. export class UnitcellEntry extends PurePluginUIComponent<{ cell: UnitcellCell }, { showOptions: boolean }> {
  126. state = { showOptions: false }
  127. componentDidMount() {
  128. this.subscribe(this.plugin.events.state.cell.stateUpdated, e => {
  129. this.forceUpdate();
  130. });
  131. }
  132. toggleVisibility = (e: React.MouseEvent<HTMLElement>) => {
  133. e.preventDefault();
  134. PluginCommands.State.ToggleVisibility(this.plugin, { state: this.props.cell.parent, ref: this.props.cell.transform.ref });
  135. e.currentTarget.blur();
  136. }
  137. highlight = (e: React.MouseEvent<HTMLElement>) => {
  138. e.preventDefault();
  139. // TODO
  140. // PluginCommands.Interactivity.Object.Highlight(this.plugin, { state: this.props.group[0].cell.parent, ref: this.props.group.map(c => c.cell.transform.ref) });
  141. }
  142. clearHighlight = (e: React.MouseEvent<HTMLElement>) => {
  143. e.preventDefault();
  144. // TODO
  145. // PluginCommands.Interactivity.ClearHighlights(this.plugin);
  146. }
  147. focus = () => {
  148. // TODO
  149. // const sphere = this.pivot.cell.obj?.data.boundary.sphere;
  150. // if (sphere) this.plugin.managers.camera.focusSphere(sphere);
  151. }
  152. toggleOptions = () => this.setState({ showOptions: !this.state.showOptions })
  153. render() {
  154. const { cell } = this.props;
  155. const { obj } = cell;
  156. if (!obj) return null;
  157. return <>
  158. <div className='msp-control-row'>
  159. <button className='msp-control-button-label' title={`Unitcell. Click to focus.`} onClick={this.focus} onMouseEnter={this.highlight} onMouseLeave={this.clearHighlight} style={{ textAlign: 'left' }}>
  160. Unitcell
  161. </button>
  162. <div className='msp-select-row'>
  163. <IconButton onClick={this.toggleVisibility} icon='visual-visibility' toggleState={!cell.state.isHidden} title={`${cell.state.isHidden ? 'Show' : 'Hide'}`} style={{ flex: '0 0 40px' }} />
  164. <IconButton onClick={this.toggleOptions} icon='cog' title='Options' toggleState={this.state.showOptions} style={{ flex: '0 0 40px' }} />
  165. </div>
  166. </div>
  167. {this.state.showOptions && <>
  168. <div className='msp-control-offset'>
  169. <UpdateTransformControl state={cell.parent} transform={cell.transform} customHeader='none' />
  170. </div>
  171. </>}
  172. </>;
  173. }
  174. }