UNPKG

pdbe-molstar

Version:
740 lines 64.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PDBeMolstarPlugin = void 0; const tslib_1 = require("tslib"); const behavior_1 = require("molstar/lib/extensions/assembly-symmetry/behavior"); const geo_export_1 = require("molstar/lib/extensions/geo-export"); const behavior_2 = require("molstar/lib/extensions/model-archive/quality-assessment/behavior"); const mp4_export_1 = require("molstar/lib/extensions/mp4-export"); const behavior_3 = require("molstar/lib/extensions/mvs/behavior"); const custom_tooltips_prop_1 = require("molstar/lib/extensions/mvs/components/custom-tooltips-prop"); const load_1 = require("molstar/lib/extensions/mvs/load"); const mvs_data_1 = require("molstar/lib/extensions/mvs/mvs-data"); const pdbe_1 = require("molstar/lib/extensions/pdbe"); const linear_algebra_1 = require("molstar/lib/mol-math/linear-algebra"); const loci_1 = require("molstar/lib/mol-model/loci"); const structure_1 = require("molstar/lib/mol-model/structure"); const assembly_unwind_1 = require("molstar/lib/mol-plugin-state/animation/built-in/assembly-unwind"); const camera_rock_1 = require("molstar/lib/mol-plugin-state/animation/built-in/camera-rock"); const camera_spin_1 = require("molstar/lib/mol-plugin-state/animation/built-in/camera-spin"); const model_index_1 = require("molstar/lib/mol-plugin-state/animation/built-in/model-index"); const spin_structure_1 = require("molstar/lib/mol-plugin-state/animation/built-in/spin-structure"); const state_interpolation_1 = require("molstar/lib/mol-plugin-state/animation/built-in/state-interpolation"); const state_snapshots_1 = require("molstar/lib/mol-plugin-state/animation/built-in/state-snapshots"); const structure_overpaint_1 = require("molstar/lib/mol-plugin-state/helpers/structure-overpaint"); const structure_representation_params_1 = require("molstar/lib/mol-plugin-state/helpers/structure-representation-params"); const objects_1 = require("molstar/lib/mol-plugin-state/objects"); const transforms_1 = require("molstar/lib/mol-plugin-state/transforms"); const model_1 = require("molstar/lib/mol-plugin-state/transforms/model"); const representation_1 = require("molstar/lib/mol-plugin-state/transforms/representation"); const index_1 = require("molstar/lib/mol-plugin-ui/index"); const react18_1 = require("molstar/lib/mol-plugin-ui/react18"); const camera_1 = require("molstar/lib/mol-plugin/behavior/dynamic/camera"); const representation_2 = require("molstar/lib/mol-plugin/behavior/dynamic/representation"); const structure_focus_representation_1 = require("molstar/lib/mol-plugin/behavior/dynamic/selection/structure-focus-representation"); const transformers_1 = require("molstar/lib/mol-plugin/behavior/dynamic/volume-streaming/transformers"); const commands_1 = require("molstar/lib/mol-plugin/commands"); const config_1 = require("molstar/lib/mol-plugin/config"); const spec_1 = require("molstar/lib/mol-plugin/spec"); const mol_state_1 = require("molstar/lib/mol-state"); const element_symbol_1 = require("molstar/lib/mol-theme/color/element-symbol"); const assets_1 = require("molstar/lib/mol-util/assets"); const color_1 = require("molstar/lib/mol-util/color/color"); const rx_event_helper_1 = require("molstar/lib/mol-util/rx-event-helper"); const custom_events_1 = require("./custom-events"); const behavior_4 = require("./domain-annotations/behavior"); const Complexes = tslib_1.__importStar(require("./extensions/complexes")); const Foldseek = tslib_1.__importStar(require("./extensions/foldseek")); const Interactions = tslib_1.__importStar(require("./extensions/interactions")); const behavior_5 = require("./extensions/state-gallery/behavior"); const manager_1 = require("./extensions/state-gallery/manager"); const ui_1 = require("./extensions/state-gallery/ui"); const helpers_1 = require("./helpers"); const plugin_custom_state_1 = require("./plugin-custom-state"); const spec_2 = require("./spec"); const spec_from_html_1 = require("./spec-from-html"); const subscribe_events_1 = require("./subscribe-events"); const superposition_1 = require("./superposition"); const superposition_focus_representation_1 = require("./superposition-focus-representation"); const pdbe_left_panel_1 = require("./ui/left-panel/pdbe-left-panel"); const pdbe_structure_controls_1 = require("./ui/pdbe-structure-controls"); const pdbe_viewport_1 = require("./ui/pdbe-viewport"); const pdbe_viewport_controls_1 = require("./ui/pdbe-viewport-controls"); const components_1 = require("./ui/split-ui/components"); const split_ui_1 = require("./ui/split-ui/split-ui"); class PDBeMolstarPlugin { constructor() { /** Helper for creating events (e.g. RxJS Subjects) */ this._ev = rx_event_helper_1.RxEventHelper.create(); this.events = { loadComplete: this._ev(), }; this.assemblyRef = ''; this.isHighlightColorUpdated = false; this.isSelectedColorUpdated = false; /** Keeps track of representations added by `.visual.select` for each structure. */ this.addedReprs = {}; /** Maps structure IDs (assigned when loading) to cell refs. */ this.structureRefMap = new Map(); /** Helper methods related to canvas and layout */ this.canvas = { /** Set canvas background color. */ setBgColor: (color) => tslib_1.__awaiter(this, void 0, void 0, function* () { if (color === undefined || color === null) return; yield this.canvas.applySettings({ color }); }), /** Set controls panel visibility. Without `isVisible` parameter, toggle controls panel visibility. */ toggleControls: (isVisible) => { if (typeof isVisible === 'undefined') isVisible = !this.plugin.layout.state.showControls; commands_1.PluginCommands.Layout.Update(this.plugin, { state: { showControls: isVisible } }); }, /** Set full-screen mode on or off. Without `isExpanded` parameter, toggle full-screen mode. */ toggleExpanded: (isExpanded) => { if (typeof isExpanded === 'undefined') isExpanded = !this.plugin.layout.state.isExpanded; commands_1.PluginCommands.Layout.Update(this.plugin, { state: { isExpanded: isExpanded } }); }, applySettings: (settings) => tslib_1.__awaiter(this, void 0, void 0, function* () { if (!settings) return; if (!this.plugin.canvas3d) return; const renderer = Object.assign({}, this.plugin.canvas3d.props.renderer); if (settings.color) { renderer.backgroundColor = (0, helpers_1.normalizeColor)(settings.color); } if (settings.lighting) { renderer.style = { name: settings.lighting }; // I don't think this does anything and I don't see how it could ever have worked } yield commands_1.PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { renderer } }); }), }; /** Helper methods related to 3D visuals */ this.visual = { /** Change the visibility of individual entity visuals */ visibility: (data) => tslib_1.__awaiter(this, void 0, void 0, function* () { var _a; if (!data) return; for (const visual in data) { const requiredVisibility = data[visual]; if (requiredVisibility === undefined) continue; const tags = (_a = helpers_1.StructureComponentTags[visual]) !== null && _a !== void 0 ? _a : []; for (const tag of tags) { const componentRef = mol_state_1.StateSelection.findTagInSubtree(this.plugin.state.data.tree, mol_state_1.StateTransform.RootRef, tag); if (componentRef) { const compVisual = this.plugin.state.data.select(componentRef)[0]; if (compVisual && compVisual.obj) { const currentlyVisible = !(compVisual.state && compVisual.state.isHidden); if (currentlyVisible !== requiredVisibility) { yield commands_1.PluginCommands.State.ToggleVisibility(this.plugin, { state: this.state, ref: componentRef }); } } } } } }), /** Change the visibility of a structure. * `structureNumberOrId` is either index (numbered from 1!) or the ID that was provided when loading the structure. * If `visibility` is undefined, toggle current visibility state. */ structureVisibility: (structureNumberOrId, visibility) => tslib_1.__awaiter(this, void 0, void 0, function* () { const struct = this.getStructure(structureNumberOrId); if (!struct) { console.error(`Cannot change visibility of structure ${structureNumberOrId}: structure not found.`); return; } const currentVisibility = !struct.cell.state.isHidden; if (visibility !== currentVisibility) { yield commands_1.PluginCommands.State.ToggleVisibility(this.plugin, { state: this.state, ref: struct.cell.transform.ref }); } }), /** With `isSpinning` parameter, switch visual rotation on or off. Without `isSpinning` parameter, toggle rotation. If `resetCamera`, also reset the camera zoom. */ toggleSpin: (isSpinning, resetCamera) => tslib_1.__awaiter(this, void 0, void 0, function* () { if (!this.plugin.canvas3d) return; const trackball = this.plugin.canvas3d.props.trackball; let toggleSpinParam = trackball.animate.name === 'spin' ? { name: 'off', params: {} } : { name: 'spin', params: { speed: 1 } }; if (typeof isSpinning !== 'undefined') { toggleSpinParam = { name: 'off', params: {} }; if (isSpinning) toggleSpinParam = { name: 'spin', params: { speed: 1 } }; } yield commands_1.PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { trackball: Object.assign(Object.assign({}, trackball), { animate: toggleSpinParam }) } }); if (resetCamera) yield commands_1.PluginCommands.Camera.Reset(this.plugin, {}); }), /** Focus (zoom) on the part of the structure defined by `selection`. * If `selection` contains more items, focus on the union of those. * `structureNumberOrId` is either index (numbered from 1!) or the ID that was provided when loading the structure; * if not provided, will use the last added structure. */ focus: (selection, structureNumberOrId) => tslib_1.__awaiter(this, void 0, void 0, function* () { const loci = this.getLociForParams(selection, structureNumberOrId); this.plugin.managers.camera.focusLoci(loci); }), /** Apply interactive focus behavior on the part of the structure defined by `data` (i.e. emulate user clicking on a substructure). * This includes behaviors like "show non-covalent interactions" or "show streamed volume data" * but not camera adjustment (call `.visual.focus` for that). * If `data` contains more items, focus on the union of those. * If `structureId` or `structureNumber` is provided, use the specified structure (numbered from 1!); * otherwise use the last added structure. */ interactivityFocus: (params) => tslib_1.__awaiter(this, void 0, void 0, function* () { var _a; const structureNumberOrId = (_a = params.structureId) !== null && _a !== void 0 ? _a : params.structureNumber; const loci = this.getLociForParams(params.data, structureNumberOrId); this.plugin.managers.structure.focus.setFromLoci(loci); }), /** Trigger highlight on the part of the structure defined by `data` * (this will look the same as when the user hovers over a part of the structure). * If `focus`, also zoom on the highlighted part. * If `structureId` or `structureNumber` is provided, use the specified structure (numbered from 1!); otherwise use the last added structure. */ highlight: (params) => tslib_1.__awaiter(this, void 0, void 0, function* () { var _a; const loci = this.getLociForParams(params.data, (_a = params.structureId) !== null && _a !== void 0 ? _a : params.structureNumber); if (loci_1.Loci.isEmpty(loci)) return; if (params.color) { yield this.visual.setColor({ highlight: params.color }); } this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci }); if (params.focus) this.plugin.managers.camera.focusLoci(loci); }), /** Remove any current highlight and reset the highlight color to its default value. */ clearHighlight: () => tslib_1.__awaiter(this, void 0, void 0, function* () { this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci: loci_1.EmptyLoci }); if (this.isHighlightColorUpdated) { yield this.visual.reset({ highlightColor: true }); } }), /** Color the parts of the structure defined by `data`. Color the rest of the structure in `nonSelectedColor` if provided. * If any items in `data` contain `focus`, zoom to the union of these items. * If any items in `data` contain `sideChain` or `representation`, add extra representations to them (colored in `representationColor` if provided). * If `structureNumber` is provided, apply to the specified structure (numbered from 1!); otherwise apply to all loaded structures. * Remove any previously added coloring and extra representations, unless `keepColors` and/or `keepRepresentations` is set. */ select: (params) => tslib_1.__awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d; const structureNumberOrId = (_a = params.structureId) !== null && _a !== void 0 ? _a : params.structureNumber; yield this.visual.clearSelection(structureNumberOrId, { keepColors: params.keepColors, keepRepresentations: params.keepRepresentations }); // Structure list to apply selection const structures = this.getStructures(structureNumberOrId); // Filter selection items that apply added representations const addedReprParams = {}; for (const param of params.data) { const repr = (_b = param.representation) !== null && _b !== void 0 ? _b : (param.sideChain ? 'ball-and-stick' : undefined); if (repr) { ((_c = addedReprParams[repr]) !== null && _c !== void 0 ? _c : (addedReprParams[repr] = [])).push(param); } } const DefaultSelectColor = color_1.Color.fromRgb(255, 112, 3); const focusLoci = []; for (const struct of structures) { const selections = this.getSelections(params.data, struct.number); for (const selection of selections) { if (selection.param.focus) { focusLoci.push(selection.loci); } } // Apply color to the main representation (color:undefined (or omitted) means apply default color, color:null means do not apply color) const overpaintLayers = selections .filter(s => s.param.color !== null) .map(s => ({ bundle: s.bundle, color: s.param.color ? (0, helpers_1.normalizeColor)(s.param.color) : DefaultSelectColor, clear: false, })); if (params.nonSelectedColor) { const wholeStructBundle = this.getBundle([{}], struct.number); if (wholeStructBundle) { overpaintLayers.unshift({ bundle: wholeStructBundle, color: (0, helpers_1.normalizeColor)(params.nonSelectedColor), clear: false, }); } } yield (0, helpers_1.applyOverpaint)(this.plugin, struct.structureRef, overpaintLayers); // Add extra representations for (const repr in addedReprParams) { const bundle = this.getBundle(addedReprParams[repr], struct.number); if (!bundle) continue; const overpaintLayers = selections.filter(s => s.param.representationColor).map(s => ({ bundle: s.bundle, color: (0, helpers_1.normalizeColor)(s.param.representationColor), clear: false, })); yield this.plugin.build() .to(struct.structureRef.cell) .apply(model_1.StructureComponent, { type: { name: 'bundle', params: bundle }, label: repr }, { tags: helpers_1.Tags.AddedComponent }) .apply(representation_1.StructureRepresentation3D, (0, structure_representation_params_1.createStructureRepresentationParams)(this.plugin, (_d = struct.structureRef.cell.obj) === null || _d === void 0 ? void 0 : _d.data, { type: repr })) .apply(transforms_1.StateTransforms.Representation.OverpaintStructureRepresentation3DFromBundle, { layers: overpaintLayers }, { tags: helpers_1.Tags.Overpaint }) .commit(); // Track that reprs have been added (for later clearSelection) this.addedReprs[struct.number] = true; } } // Apply focus if (focusLoci.length > 0) { this.plugin.managers.camera.focusLoci(focusLoci); } }), /** Remove any coloring and extra representations previously added by the `select` method. * `structureNumberOrId` is either index (numbered from 1!) or the ID that was provided when loading the structure; * if not provided, will apply to all loaded structures. * If `keepColors`, current residue coloring is preserved. If `keepRepresentations`, current added representations are preserved. */ clearSelection: (structureNumberOrId, options) => tslib_1.__awaiter(this, void 0, void 0, function* () { // Structure list to apply to const structures = this.getStructures(structureNumberOrId); for (const struct of structures) { // Remove overpaint if (!(options === null || options === void 0 ? void 0 : options.keepColors)) { const componentsToClear = struct.structureRef.components.filter(c => { var _a; return !((_a = c.cell.transform.tags) === null || _a === void 0 ? void 0 : _a.includes(helpers_1.Tags.AddedComponent)); }); yield (0, structure_overpaint_1.clearStructureOverpaint)(this.plugin, componentsToClear); } // Remove added reprs if (!(options === null || options === void 0 ? void 0 : options.keepRepresentations)) { if (this.addedReprs[struct.number]) { const componentsToDelete = struct.structureRef.components.filter(comp => { var _a; return (_a = comp.cell.transform.tags) === null || _a === void 0 ? void 0 : _a.includes(helpers_1.Tags.AddedComponent); }); const update = this.plugin.build(); for (const comp of componentsToDelete) { update.delete(comp.cell.transform.ref); } yield update.commit(); delete this.addedReprs[struct.number]; } } } }), /** Add interactive tooltips to parts of the structure. The added tooltips will be shown on a separate line in the tooltip box. * Repeated call to this function removes any previously added tooltips. * `structureNumber` counts from 1; if not provided, tooltips will be applied to all loaded structures. * Example: `await this.visual.tooltips({ data: [{ struct_asym_id: 'A', tooltip: 'Chain A' }, { struct_asym_id: 'B', tooltip: 'Chain B' }] });`. */ tooltips: (params) => tslib_1.__awaiter(this, void 0, void 0, function* () { var _a; // Structure list to apply tooltips const structures = this.getStructures((_a = params.structureId) !== null && _a !== void 0 ? _a : params.structureNumber); for (const struct of structures) { const selections = this.getSelections(params.data, struct.number); const customTooltipProps = { tooltips: selections.map(s => { var _a; return ({ text: (_a = s.param.tooltip) !== null && _a !== void 0 ? _a : '', selector: { name: 'bundle', params: s.bundle } }); }), }; const structRef = struct.structureRef.cell.transform.ref; let customPropsCells = this.plugin.state.data.select(mol_state_1.StateSelection.Generators.ofTransformer(model_1.CustomStructureProperties, structRef)); if (customPropsCells.length === 0) { yield this.plugin.build().to(structRef).apply(model_1.CustomStructureProperties).commit(); customPropsCells = this.plugin.state.data.select(mol_state_1.StateSelection.Generators.ofTransformer(model_1.CustomStructureProperties, structRef)); } yield this.plugin.build().to(customPropsCells[0]).update(old => ({ properties: Object.assign(Object.assign({}, old.properties), { [custom_tooltips_prop_1.CustomTooltipsProvider.descriptor.name]: customTooltipProps }), autoAttach: old.autoAttach.includes(custom_tooltips_prop_1.CustomTooltipsProvider.descriptor.name) ? old.autoAttach : [...old.autoAttach, custom_tooltips_prop_1.CustomTooltipsProvider.descriptor.name], })).commit(); } }), /** Remove any custom tooltips added by the `tooltips` method. */ clearTooltips: (structureNumberOrId) => tslib_1.__awaiter(this, void 0, void 0, function* () { yield this.visual.tooltips({ data: [], structureId: structureNumberOrId }); }), /** Set highlight and/or selection color. * Highlight color is used when the user hovers over a part of the structure or when applying the `highlight` method. * Selection color is used when creating selections with Selection Mode (the mouse cursor icon) and is not related to the color used by the `select` method. */ setColor: (params) => tslib_1.__awaiter(this, void 0, void 0, function* () { if (!this.plugin.canvas3d) return; if (!params.highlight && !params.select) return; const renderer = Object.assign({}, this.plugin.canvas3d.props.renderer); const marking = Object.assign({}, this.plugin.canvas3d.props.marking); if (params.highlight) { renderer.highlightColor = (0, helpers_1.normalizeColor)(params.highlight); marking.highlightEdgeColor = color_1.Color.darken((0, helpers_1.normalizeColor)(params.highlight), 1); this.isHighlightColorUpdated = true; } if (params.select) { renderer.selectColor = (0, helpers_1.normalizeColor)(params.select); marking.selectEdgeColor = color_1.Color.darken((0, helpers_1.normalizeColor)(params.select), 1); this.isSelectedColorUpdated = true; } yield commands_1.PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { renderer, marking } }); }), /** Reset various settings to defaults: * `camera` resets camera position (i.e. zooms on the whole scene). * `theme` resets color theme for visual representations. * `highlightColor` and `selectColor` reset colors previously set by the `setColor` method. */ reset: (params) => tslib_1.__awaiter(this, void 0, void 0, function* () { if (params.camera) yield commands_1.PluginCommands.Camera.Reset(this.plugin, { durationMs: 250 }); if (params.theme) { const defaultTheme = { color: this.defaultColorTheme() }; const componentGroups = this.plugin.managers.structure.hierarchy.currentComponentGroups; for (const compGrp of componentGroups) { yield this.plugin.managers.structure.component.updateRepresentationsTheme(compGrp, defaultTheme); } } if (params.highlightColor || params.selectColor) { if (!this.plugin.canvas3d) return; const renderer = Object.assign({}, this.plugin.canvas3d.props.renderer); const marking = Object.assign({}, this.plugin.canvas3d.props.marking); if (params.highlightColor) { renderer.highlightColor = this.defaultRendererProps.highlightColor; marking.highlightEdgeColor = this.defaultMarkingProps.highlightEdgeColor; this.isHighlightColorUpdated = false; } if (params.selectColor) { renderer.selectColor = this.defaultRendererProps.selectColor; marking.selectEdgeColor = this.defaultMarkingProps.selectEdgeColor; this.isSelectedColorUpdated = false; } yield commands_1.PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { renderer, marking } }); } }), setViewDirection: (view, options) => tslib_1.__awaiter(this, void 0, void 0, function* () { var _a; const visibleSphere = (_a = this.plugin.canvas3d) === null || _a === void 0 ? void 0 : _a.boundingSphereVisible; if (!visibleSphere) return; const rot = (0, helpers_1.getRotationMat4)(view); const snapshot = this.plugin.canvas3d.camera.getFocus(visibleSphere.center, visibleSphere.radius); // Need to force direction and up after getFocus (getFocus sometimes flips direction/up) const newDir = linear_algebra_1.Vec3.create(0, 0, -1); linear_algebra_1.Vec3.transformMat4(newDir, newDir, rot); linear_algebra_1.Vec3.scale(newDir, newDir, linear_algebra_1.Vec3.distance(snapshot.position, snapshot.target)); snapshot.position = linear_algebra_1.Vec3.sub(newDir, snapshot.target, newDir); const newUp = linear_algebra_1.Vec3.create(0, 1, 0); snapshot.up = linear_algebra_1.Vec3.transformMat4(newUp, newUp, rot); yield commands_1.PluginCommands.Camera.SetSnapshot(this.plugin, { snapshot, durationMs: options === null || options === void 0 ? void 0 : options.durationMs }); }), /** Change parameters of the plugin instance. * Can be used to load a different structure. * If `fullLoad`, remove currently loaded structure before loading the new one; * otherwise add the new structure to existing structures. */ update: (options, fullLoad) => tslib_1.__awaiter(this, void 0, void 0, function* () { console.debug('Updating PDBeMolstarPlugin instance with options:', options); // Validate options if (!options) { console.error('Missing `options` argument to `PDBeMolstarPlugin.visual.update'); return false; } const validationIssues = (0, spec_2.validateInitParams)(options); if (validationIssues) { console.error('Invalid PDBeMolstarPlugin options:', options); return false; } this.initParams = (0, helpers_1.addDefaults)(options, spec_2.DefaultParams); if (!this.initParams.moleculeId && !this.initParams.customData) return false; if (this.initParams.customData && this.initParams.customData.url && !this.initParams.customData.format) return false; (0, plugin_custom_state_1.PluginCustomState)(this.plugin).initParams = this.initParams; // Update layout commands_1.PluginCommands.Layout.Update(this.plugin, { state: (0, helpers_1.pluginLayoutStateFromInitParams)(this.initParams) }); // Show/hide buttons in the viewport control panel this.plugin.config.set(config_1.PluginConfig.Viewport.ShowScreenshotControls, !this.initParams.hideCanvasControls.includes('screenshot')); this.plugin.config.set(config_1.PluginConfig.Viewport.ShowExpand, !this.initParams.hideCanvasControls.includes('expand')); this.plugin.config.set(config_1.PluginConfig.Viewport.ShowControls, !this.initParams.hideCanvasControls.includes('controlToggle')); this.plugin.config.set(config_1.PluginConfig.Viewport.ShowSettings, !this.initParams.hideCanvasControls.includes('controlInfo')); this.plugin.config.set(config_1.PluginConfig.Viewport.ShowSelectionMode, !this.initParams.hideCanvasControls.includes('selection')); this.plugin.config.set(config_1.PluginConfig.Viewport.ShowAnimation, !this.initParams.hideCanvasControls.includes('animation')); this.plugin.config.set(config_1.PluginConfig.Viewport.ShowTrajectoryControls, !this.initParams.hideCanvasControls.includes('trajectory')); // Set background colour if (this.initParams.bgColor || this.initParams.lighting) { yield this.canvas.applySettings({ color: this.initParams.bgColor, lighting: this.initParams.lighting }); } // Load Molecule CIF or coordQuery and Parse const dataSource = this.getMoleculeSrcUrl(); if (dataSource) { yield this.load({ url: dataSource.url, format: dataSource.format, assemblyId: this.initParams.assemblyId, isBinary: dataSource.isBinary }, fullLoad); } return true; }), }; } /** Extract InitParams from attributes of an HTML element */ static initParamsFromHtmlAttributes(element) { return (0, spec_from_html_1.initParamsFromHtmlAttributes)(element); } render(target, options) { return tslib_1.__awaiter(this, void 0, void 0, function* () { var _a, _b; console.debug('Rendering PDBeMolstarPlugin instance with options:', options); // Validate options if (!options) { console.error('Missing `options` argument to `PDBeMolstarPlugin.render'); return; } const validationIssues = (0, spec_2.validateInitParams)(options); if (validationIssues) { console.error('Invalid PDBeMolstarPlugin options:', options); return; } this.initParams = (0, helpers_1.addDefaults)(options, spec_2.DefaultParams); // Set PDBe Plugin Spec const pdbePluginSpec = (0, spec_2.DefaultPluginUISpec)(); (_a = pdbePluginSpec.config) !== null && _a !== void 0 ? _a : (pdbePluginSpec.config = []); pdbePluginSpec.behaviors.push(spec_1.PluginSpec.Behavior(behavior_3.MolViewSpec)); pdbePluginSpec.behaviors.push(spec_1.PluginSpec.Behavior(pdbe_1.PDBeStructureQualityReport, { autoAttach: false, showTooltip: false })); pdbePluginSpec.behaviors.push(spec_1.PluginSpec.Behavior(behavior_4.PDBeDomainAnnotations, { autoAttach: false, showTooltip: false })); pdbePluginSpec.behaviors.push(spec_1.PluginSpec.Behavior(behavior_1.AssemblySymmetry)); pdbePluginSpec.config.push([behavior_1.AssemblySymmetryConfig.DefaultServerType, 'pdbe'], [behavior_1.AssemblySymmetryConfig.DefaultServerUrl, 'https://www.ebi.ac.uk/pdbe/aggregated-api/pdb/symmetry'], [behavior_1.AssemblySymmetryConfig.ApplyColors, false]); if (this.initParams.galleryView) { pdbePluginSpec.behaviors.push(spec_1.PluginSpec.Behavior(behavior_5.StateGallery)); } if (!this.initParams.ligandView && !this.initParams.superposition && this.initParams.selectInteraction) { pdbePluginSpec.behaviors.push(spec_1.PluginSpec.Behavior(structure_focus_representation_1.StructureFocusRepresentation)); } if (this.initParams.superposition) { pdbePluginSpec.behaviors.push(spec_1.PluginSpec.Behavior(superposition_focus_representation_1.SuperpositionFocusRepresentation), spec_1.PluginSpec.Behavior(behavior_2.MAQualityAssessment, { autoAttach: true, showTooltip: true })); } pdbePluginSpec.layout = { initial: (0, helpers_1.pluginLayoutStateFromInitParams)(this.initParams), }; pdbePluginSpec.components = { controls: { left: this.initParams.tabs === 'pdbe' ? pdbe_left_panel_1.PDBeLeftPanelControls : pdbe_left_panel_1.DefaultLeftPanelControls, }, viewport: { controls: pdbe_viewport_controls_1.PDBeViewportControls, view: pdbe_viewport_1.PDBeViewport_NoFullscreen, }, remoteState: 'none', structureTools: this.initParams.superposition ? pdbe_structure_controls_1.PDBeSuperpositionStructureTools : this.initParams.ligandView ? pdbe_structure_controls_1.PDBeLigandViewStructureTools : this.initParams.galleryView ? ui_1.StateGalleryControls : pdbe_structure_controls_1.PDBeStructureTools, }; if (this.initParams.alphafoldView) { pdbePluginSpec.behaviors.push(spec_1.PluginSpec.Behavior(behavior_2.MAQualityAssessment, { autoAttach: true, showTooltip: true })); } pdbePluginSpec.config.push([config_1.PluginConfig.Structure.DefaultRepresentationPresetParams, { theme: { globalName: (this.initParams.alphafoldView) ? 'plddt-confidence' : undefined, carbonColor: { name: 'element-symbol', params: {} }, focus: { name: 'element-symbol', params: { carbonColor: { name: 'element-symbol', params: {} } }, }, }, }]); element_symbol_1.ElementSymbolColorThemeParams.carbonColor.defaultValue = { name: 'element-symbol', params: {} }; // Add animation props if (!this.initParams.ligandView && !this.initParams.superposition) { pdbePluginSpec.animations = [model_index_1.AnimateModelIndex, camera_spin_1.AnimateCameraSpin, camera_rock_1.AnimateCameraRock, state_snapshots_1.AnimateStateSnapshots, assembly_unwind_1.AnimateAssemblyUnwind, spin_structure_1.AnimateStructureSpin, state_interpolation_1.AnimateStateInterpolation]; pdbePluginSpec.behaviors.push(spec_1.PluginSpec.Behavior(mp4_export_1.Mp4Export)); pdbePluginSpec.behaviors.push(spec_1.PluginSpec.Behavior(geo_export_1.GeometryExport)); } if (this.initParams.hideCanvasControls.includes('screenshot')) pdbePluginSpec.config.push([config_1.PluginConfig.Viewport.ShowScreenshotControls, false]); if (this.initParams.hideCanvasControls.includes('expand')) pdbePluginSpec.config.push([config_1.PluginConfig.Viewport.ShowExpand, false]); if (this.initParams.hideCanvasControls.includes('controlToggle')) pdbePluginSpec.config.push([config_1.PluginConfig.Viewport.ShowControls, false]); if (this.initParams.hideCanvasControls.includes('controlInfo')) pdbePluginSpec.config.push([config_1.PluginConfig.Viewport.ShowSettings, false]); if (this.initParams.hideCanvasControls.includes('selection')) pdbePluginSpec.config.push([config_1.PluginConfig.Viewport.ShowSelectionMode, false]); if (this.initParams.hideCanvasControls.includes('animation')) pdbePluginSpec.config.push([config_1.PluginConfig.Viewport.ShowAnimation, false]); if (this.initParams.hideCanvasControls.includes('trajectory')) pdbePluginSpec.config.push([config_1.PluginConfig.Viewport.ShowTrajectoryControls, false]); // override default event bindings if (this.initParams.selectBindings) { pdbePluginSpec.behaviors.push(spec_1.PluginSpec.Behavior(representation_2.SelectLoci, { bindings: this.initParams.selectBindings })); } if (this.initParams.focusBindings) { pdbePluginSpec.behaviors.push(spec_1.PluginSpec.Behavior(camera_1.FocusLoci, { bindings: this.initParams.focusBindings })); } // Create/ Initialise Plugin const onBeforeUIRender = (plugin) => tslib_1.__awaiter(this, void 0, void 0, function* () { // This needs to run after the plugin is created but before the UI is rendered (0, plugin_custom_state_1.PluginCustomState)(plugin).initParams = Object.assign({}, this.initParams); (0, plugin_custom_state_1.PluginCustomState)(plugin).events = { segmentUpdate: this._ev(), superpositionInit: this._ev(), isBusy: this._ev(), }; if (this.initParams.galleryView && this.initParams.moleculeId) { try { yield manager_1.StateGalleryManager.create(plugin, this.initParams.moleculeId); // sets extension custom state } catch (err) { console.error(`Failed to initialize StateGalleryManager for entry ${this.initParams.moleculeId}.\n${err}`); } } }); if (Array.isArray(target)) { this.targetElement = (0, split_ui_1.resolveHTMLElement)(target[0].target); this.plugin = yield (0, split_ui_1.createPluginSplitUI)({ layout: target, spec: pdbePluginSpec, onBeforeUIRender, }); for (const comp of target) { try { (0, split_ui_1.resolveHTMLElement)(comp.target).viewerInstance = this; } catch ( /* some of the target elements missing, ignore */_c) { /* some of the target elements missing, ignore */ } } } else { this.targetElement = (0, split_ui_1.resolveHTMLElement)(target); this.plugin = yield (0, index_1.createPluginUI)({ target: this.targetElement, spec: pdbePluginSpec, onBeforeUIRender, render: react18_1.renderReact18 }); this.targetElement.viewerInstance = this; } // Set background colour if (this.initParams.bgColor || this.initParams.lighting) { this.canvas.applySettings({ color: this.initParams.bgColor, lighting: this.initParams.lighting }); } // Set selection granularity if (this.initParams.granularity) { this.plugin.managers.interactivity.setProps({ granularity: this.initParams.granularity }); } // Set default highlight and selection colors if (this.initParams.highlightColor || this.initParams.selectColor) { this.visual.setColor({ highlight: this.initParams.highlightColor, select: this.initParams.selectColor }); } // Save renderer defaults this.defaultRendererProps = Object.assign({}, this.plugin.canvas3d.props.renderer); this.defaultMarkingProps = Object.assign({}, this.plugin.canvas3d.props.marking); if (this.initParams.superposition) { // Set left panel tab this.plugin.behaviors.layout.leftPanelTabName.next('segments'); // Initialise superposition (0, superposition_1.initSuperposition)(this.plugin, this.events.loadComplete); } else if (this.initParams.galleryView) { // Do nothing, StateGalleryManager initialized in onBeforeUIRender } else { // Load Molecule CIF or coordQuery and Parse const dataSource = this.getMoleculeSrcUrl(); if (dataSource) { this.load({ url: dataSource.url, format: dataSource.format, assemblyId: this.initParams.assemblyId, isBinary: dataSource.isBinary, progressMessage: `Loading ${(_b = this.initParams.moleculeId) !== null && _b !== void 0 ? _b : ''} ...`, id: PDBeMolstarPlugin.MAIN_STRUCTURE_ID, }); } // Subscribe to events from other PDB Component if (this.initParams.subscribeEvents) { (0, subscribe_events_1.subscribeToComponentEvents)(this); } // Emit events for other PDB Components custom_events_1.CustomEvents.add(this.plugin, this.targetElement); } }); } getMoleculeSrcUrl() { if (this.initParams.customData) { let { url, format, binary } = this.initParams.customData; if (!url || !format) { throw new Error(`Provide all custom data parameters`); } if (format === 'cif' || format === 'bcif') format = 'mmcif'; // Validate supported format const supportedFormats = ['mmcif', 'pdb', 'sdf']; if (!supportedFormats.includes(format)) { throw new Error(`${format} not supported.`); } return { url: url, format: format, isBinary: binary, }; } if (this.initParams.moleculeId) { const request = { pdbId: this.initParams.moleculeId, queryType: 'full', queryParams: {} }; if (this.initParams.ligandView) { request.queryType = 'residueSurroundings'; request.queryParams['data_source'] = 'pdb-h'; if (!this.initParams.ligandView.label_comp_id_list) { request.queryParams['label_comp_id'] = this.initParams.ligandView.label_comp_id; request.queryParams['auth_seq_id'] = this.initParams.ligandView.auth_seq_id; request.queryParams['auth_asym_id'] = this.initParams.ligandView.auth_asym_id; } } return { url: (0, helpers_1.getStructureUrl)(this.initParams, request), format: 'mmcif', isBinary: this.initParams.encoding === 'bcif', }; } throw new Error(`Mandatory parameters missing! (customData or moleculeId must be defined)`); } get state() { return this.plugin.state.data; } createLigandStructure(isBranched) { return tslib_1.__awaiter(this, void 0, void 0, function* () { var _a, e_1, _b, _c; var _d, _e; if (this.assemblyRef === '') return; try { for (var _f = true, _g = tslib_1.__asyncValues(this.plugin.managers.structure.hierarchy.currentComponentGroups), _h; _h = yield _g.next(), _a = _h.done, !_a; _f = true) { _c = _h.value; _f = false; const comp = _c; yield commands_1.PluginCommands.State.RemoveObject(this.plugin, { state: comp[0].cell.parent, ref: comp[0].cell.transform.ref, removeParentGhosts: true }); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (!_f && !_a && (_b = _g.return)) yield _b.call(_g); } finally { if (e_1) throw e_1.error; } } const structure = this.state.select(this.assemblyRef)[0]; let ligandQuery; if (isBranched) { ligandQuery = helpers_1.LigandView.branchedQuery((_d = this.initParams.ligandView) === null || _d === void 0 ? void 0 : _d.label_comp_id_list); } else { ligandQuery = helpers_1.LigandView.query(this.initParams.ligandView); } const ligandVis = yield this.plugin.builders.structure.tryCreateComponentFromExpression(structure, ligandQuery.core, 'pivot', { label: 'Ligand' }); if (ligandVis) yield this.plugin.builders.structure.representation.addRepresentation(ligandVis, { type: 'ball-and-stick', color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } }, size: 'uniform', sizeParams: { value: 2.5 } }, { tag: 'ligand-vis' }); const ligandSurr = yield this.plugin.builders.structure.tryCreateComponentFromExpression(structure, ligandQuery.surroundings, 'rest', { label: 'Surroundings' }); if (ligandSurr) yield this.plugin.builders.structure.representation.addRepresentation(ligandSurr, { type: 'ball-and-stick', color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } }, size: 'uniform', sizeParams: { value: 0.8 } }); // Focus ligand const ligRef = mol_state_1.StateSelection.findTagInSubtree(this.plugin.state.data.tree, mol_state_1.StateTransform.RootRef, 'ligand-vis'); if (!ligRef) return; const cell = this.plugin.state.data.cells.get(ligRef); if (cell === null || cell === void 0 ? void 0 : cell.obj) { const repr = cell.obj.data.repr; const ligLoci = repr.getAllLoci()[0]; // getAllLoci returns multiple copies of the same loci (one per representation visual) this.plugin.managers.structure.focus.setFromLoci(ligLoci); // focus-add is not handled in camera behavior, doing it here const current = (_e = this.plugin.managers.structure.focus.current) === null || _e === void 0 ? void 0 : _e.loci; if (current) this.plugin.managers.camera.focusLoci(current); } }); } load(_a) { return tslib_1.__awaiter(this, arguments, void 0, function* ({ url, format = 'mmcif', isBinary = false, assemblyId = '', progressMessage, id }, fullLoad = true) { yield (0, helpers_1.runWithProgressMessage)(this.plugin, progressMessage, () => tslib_1.__awaiter(this, void 0, void 0, function* () { var _a, _b; let success = false; try { (_a = (0, plugin_custom_state_1.PluginCustomState)(this.plugin).events) === null || _a === void 0 ? void 0 : _a.isBusy.next(true); if (fullLoad) yield this.clear(); const isHetView = this.initParams.ligandView ? true : false; let downloadOptions = undefined; let isBranchedView = false; if (this.initParams.ligandView && this.initParams.ligandView.label_comp_id_list) { isBranchedView = true; downloadOptions = { body: JSON.stringify(this.initParams.ligandView.label_comp_id_list), headers: [['Content-type', 'app