UNPKG

pdbe-molstar

Version:
226 lines (225 loc) 12.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SymmetryAnnotationControls = void 0; exports.isAssemblySymmetryAnnotationApplicable = isAssemblySymmetryAnnotationApplicable; const tslib_1 = require("tslib"); const jsx_runtime_1 = require("react/jsx-runtime"); const behavior_1 = require("molstar/lib/extensions/assembly-symmetry/behavior"); const prop_1 = require("molstar/lib/extensions/assembly-symmetry/prop"); const base_1 = require("molstar/lib/mol-plugin-ui/base"); const mol_state_1 = require("molstar/lib/mol-state"); const mol_task_1 = require("molstar/lib/mol-task"); const param_definition_1 = require("molstar/lib/mol-util/param-definition"); const annotation_row_controls_1 = require("./annotation-row-controls"); const DefaultSymmetryParams = { /** State of the visibility button */ on: param_definition_1.ParamDefinition.Boolean(false, { isHidden: true }), /** Index of the currently selected symmetry (in case there a more symmetries for an assembly), regardless of whether visibility is on of off */ symmetryIndex: param_definition_1.ParamDefinition.Select(0, [[0, 'Auto']]), /** `true` if visibility is on and symmetry data have been retrieved and contain at least one non-trivial symmetry. */ hasSymmetries: param_definition_1.ParamDefinition.Boolean(false, { isHidden: true }), }; const DefaultSymmetryControlsState = { params: DefaultSymmetryParams, values: param_definition_1.ParamDefinition.getDefaultValues(DefaultSymmetryParams), }; /** UI controls for showing Assembly Symmetry annotations (a row within Annotations section) */ class SymmetryAnnotationControls extends base_1.PurePluginUIComponent { constructor() { super(...arguments); this.state = DefaultSymmetryControlsState; this.currentStructureId = undefined; } componentDidMount() { // Reset state when the pivot structure changes this.subscribe(this.plugin.managers.structure.hierarchy.behaviors.selection, c => { var _a; const structureObj = (_a = c.structures[0]) === null || _a === void 0 ? void 0 : _a.cell.obj; const structureId = structureObj === null || structureObj === void 0 ? void 0 : structureObj.id; if (structureId !== this.currentStructureId) { this.currentStructureId = structureId; this.syncParams(); } }); // Synchronize params when AssemblySymmetry3D changes this.subscribe(this.plugin.state.events.cell.stateUpdated, e => { if (e.cell.transform.transformer === behavior_1.AssemblySymmetry3D) { this.syncParams(); } }); // Synchronize params when AssemblySymmetry3D is removed this.subscribe(this.plugin.state.events.cell.removed, e => { this.syncParams(); }); } /** Synchronize parameters and values in UI with real parameters currently applied in `AssemblySymmetryProvider` */ syncParams() { if (this.hasAssemblySymmetry3D()) { const hasSymmetries = this.hasSymmetriesAvailable(); const realParams = this.getRealParams(); const realValues = this.getRealValues(); const options = hasSymmetries ? realParams.symmetryIndex.options.filter(([index, label]) => index >= 0) // Removing the 'off' option (having index -1) : [[0, 'None']]; const params = Object.assign(Object.assign({}, this.state.params), { symmetryIndex: param_definition_1.ParamDefinition.Select(0, options) }); const values = (realValues.symmetryIndex >= 0) ? { on: true, symmetryIndex: realValues.symmetryIndex, hasSymmetries, } : { on: false, symmetryIndex: this.state.values.symmetryIndex, hasSymmetries, }; this.setState({ params, values }); } else { this.setState({ params: DefaultSymmetryControlsState.params, values: DefaultSymmetryControlsState.values, }); } } /** Return `true` if symmetry data have been retrieved and contain at least one non-trivial symmetry. */ hasSymmetriesAvailable() { var _a, _b; const structure = (_b = (_a = this.getPivotStructure()) === null || _a === void 0 ? void 0 : _a.cell.obj) === null || _b === void 0 ? void 0 : _b.data; const symmetryData = structure && prop_1.AssemblySymmetryDataProvider.get(structure).value; return symmetryData !== undefined && symmetryData.filter(sym => sym.symbol !== 'C1').length > 0; } /** Get the first loaded structure, if any. */ getPivotStructure() { return getPivotStructure(this.plugin); } /** Get parameters currently applied in `AssemblySymmetryProvider` */ getRealParams() { var _a, _b; const structure = (_b = (_a = this.getPivotStructure()) === null || _a === void 0 ? void 0 : _a.cell.obj) === null || _b === void 0 ? void 0 : _b.data; let params; if (structure) { params = prop_1.AssemblySymmetryProvider.getParams(structure); } else { params = prop_1.AssemblySymmetryProvider.defaultParams; } return params; } /** Get parameter values currently applied in `AssemblySymmetryProvider` */ getRealValues() { var _a, _b; const structure = (_b = (_a = this.getPivotStructure()) === null || _a === void 0 ? void 0 : _a.cell.obj) === null || _b === void 0 ? void 0 : _b.data; if (structure) { return prop_1.AssemblySymmetryProvider.props(structure); } else { return Object.assign(Object.assign({}, param_definition_1.ParamDefinition.getDefaultValues(prop_1.AssemblySymmetryProvider.defaultParams)), { symmetryIndex: -1 }); } } /** Return `true` if an `AssemblySymmetry3D` node existing in the */ hasAssemblySymmetry3D() { const struct = this.getPivotStructure(); const state = struct === null || struct === void 0 ? void 0 : struct.cell.parent; return state !== undefined && !!mol_state_1.StateSelection.findTagInSubtree(state.tree, struct.cell.transform.ref, prop_1.AssemblySymmetryData.Tag.Representation); } /** Run changes needed to set visibility on or off, and set UI accordingly */ apply(applied) { return tslib_1.__awaiter(this, void 0, void 0, function* () { if (applied) { if (this.hasAssemblySymmetry3D()) { yield this.changeParamValues(Object.assign(Object.assign({}, this.state.values), { on: true })); } else { yield this.initSymmetry(); } this.setState(old => ({ values: Object.assign(Object.assign({}, old.values), { on: true }) })); } else { if (this.hasAssemblySymmetry3D()) { yield this.changeParamValues(Object.assign(Object.assign({}, this.state.values), { on: false })); } else { // no action needed } this.setState(old => ({ values: Object.assign(Object.assign({}, old.values), { on: false }) })); } }); } /** Run changes needed to change parameter values, and set UI accordingly */ changeParamValues(values) { return tslib_1.__awaiter(this, void 0, void 0, function* () { var _a; const struct = this.getPivotStructure(); if (!struct) return; const currValues = this.getRealValues(); const newValues = Object.assign(Object.assign({}, currValues), { symmetryIndex: values.on ? values.symmetryIndex : -1 }); if (struct.properties) { const b = this.plugin.state.data.build(); b.to(struct.properties.cell).update(old => { old.properties[prop_1.AssemblySymmetryProvider.descriptor.name] = newValues; }); yield b.commit(); } else { const pd = this.plugin.customStructureProperties.getParams((_a = struct.cell.obj) === null || _a === void 0 ? void 0 : _a.data); const params = param_definition_1.ParamDefinition.getDefaultValues(pd); params.properties[prop_1.AssemblySymmetryProvider.descriptor.name] = newValues; yield this.plugin.builders.structure.insertStructureProperties(struct.cell, params); } if (!values.on) { // if values.on, parameters will be updated via subscription this.setState({ values }); } }); } /** Try to retrieve symmetry data and create `AssemblySymmetry3D` representation */ initSymmetry(initialSymmetryIndex) { return tslib_1.__awaiter(this, void 0, void 0, function* () { yield mol_task_1.Task.create('Initialize Assembly Symmetry', (ctx) => tslib_1.__awaiter(this, void 0, void 0, function* () { var _a; const struct = this.getPivotStructure(); const data = (_a = struct === null || struct === void 0 ? void 0 : struct.cell.obj) === null || _a === void 0 ? void 0 : _a.data; if (!data) return; try { const propCtx = { runtime: ctx, assetManager: this.plugin.managers.asset }; const config = (0, behavior_1.getAssemblySymmetryConfig)(this.plugin); const symmetryDataProps = { serverType: config.DefaultServerType, serverUrl: config.DefaultServerUrl, }; yield prop_1.AssemblySymmetryDataProvider.attach(propCtx, data, symmetryDataProps); const assemblySymmetryData = prop_1.AssemblySymmetryDataProvider.get(data).value; const symmetryIndex = initialSymmetryIndex !== null && initialSymmetryIndex !== void 0 ? initialSymmetryIndex : (assemblySymmetryData ? prop_1.AssemblySymmetryData.firstNonC1(assemblySymmetryData) : -1); const symmetryProps = Object.assign(Object.assign({}, symmetryDataProps), { symmetryIndex }); yield prop_1.AssemblySymmetryProvider.attach(propCtx, data, symmetryProps); } catch (e) { this.plugin.log.error(`Assembly Symmetry: ${e}`); return; } yield (0, behavior_1.tryCreateAssemblySymmetry)(this.plugin, struct.cell); })).run(); }); } render() { const notAvailable = this.state.values.on && !this.state.values.hasSymmetries; const shortTitle = 'Assembly Symmetry' + (notAvailable ? ' [Not Available]' : ''); const title = 'Assembly Symmetry' + (notAvailable ? ' [Not Available]\nSymmetry information for this assembly is not available' : ''); return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(annotation_row_controls_1.AnnotationRowControls, { shortTitle: shortTitle, title: title, applied: this.state.values.on, onChangeApplied: applied => this.apply(applied), params: this.state.params, values: this.state.values, onChangeValues: v => this.changeParamValues(v), errorMessage: !this.state.values.on ? 'First activate annotation to show options' : notAvailable ? 'Symmetry information not available' : undefined }) }); } } exports.SymmetryAnnotationControls = SymmetryAnnotationControls; /** Get the first loaded structure, if any. */ function getPivotStructure(plugin) { return plugin.managers.structure.hierarchy.selection.structures[0]; } function isAssemblySymmetryAnnotationApplicable(plugin) { var _a, _b, _c; const struct = getPivotStructure(plugin); const isAssembly = ((_b = (_a = struct === null || struct === void 0 ? void 0 : struct.cell.obj) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.units[0].conformation.operator.assembly) !== undefined; return isAssembly && prop_1.AssemblySymmetryData.isApplicable((_c = struct === null || struct === void 0 ? void 0 : struct.cell.obj) === null || _c === void 0 ? void 0 : _c.data); // It would be nice to disable the default `AssemblySymmetry.isApplicable` behavior // (i.e. hiding Assembly Symmetry controls for non-biological assemblies, e.g. 1smv assembly 3) // by `AssemblySymmetry.isApplicable = struct => struct?.units[0].conformation.operator.assembly !== undefined;` // But we cannot easily override the `fetch` function which calls the original `isApplicable`. }