UNPKG

pdbe-molstar

Version:
697 lines (696 loc) 74.9 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 structure_transparency_1 = require("molstar/lib/mol-plugin-state/helpers/structure-transparency"); 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 names_1 = require("molstar/lib/mol-util/color/names"); 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 behavior_6 = require("./sequence-color/behavior"); const sequence_color_annotations_prop_1 = require("./sequence-color/sequence-color-annotations-prop"); const sequence_color_theme_prop_1 = require("./sequence-color/sequence-color-theme-prop"); 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!; use negative index to count backwards) 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!; use negative index to count backwards) 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!; use negative index to count backwards); * 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!; use negative index to count backwards); 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 `opacity`, set opacity accordingly, set opacity to `nonSelectedOpacity` (or 1) for the rest of the structure. * 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!; use negative index to count backwards); otherwise apply to all loaded structures. * Remove any previously added coloring, opacity, and/or extra representations, unless `keepColors`, `keepOpacity`, 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, keepOpacity: params.keepOpacity, 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); // Apply opacity to the main representation const transparencyLayers = selections .filter(s => s.param.opacity !== undefined) .map(s => ({ bundle: s.bundle, value: 1 - s.param.opacity, })); if (params.nonSelectedOpacity !== undefined) { const wholeStructBundle = this.getBundle([{}], struct.number); if (wholeStructBundle) { transparencyLayers.unshift({ bundle: wholeStructBundle, value: 1 - params.nonSelectedOpacity, }); } } yield (0, helpers_1.applyTransparency)(this.plugin, struct.structureRef, transparencyLayers); // Add extra representations for (const repr in addedReprParams) { const bundle = this.getBundle(addedReprParams[repr], struct.number); if (!bundle) continue; const update = this.plugin.build(); const addedRepr = update.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 })); const overpaintLayers = selections .filter(s => s.param.representationColor) .map(s => ({ bundle: s.bundle, color: (0, helpers_1.normalizeColor)(s.param.representationColor), clear: false, })); if (overpaintLayers.length > 0) { addedRepr.apply(transforms_1.StateTransforms.Representation.OverpaintStructureRepresentation3DFromBundle, { layers: overpaintLayers }, { tags: helpers_1.Tags.Overpaint }); } const transparencyLayers = selections .filter(s => s.param.representationOpacity !== undefined) .map(s => ({ bundle: s.bundle, value: 1 - s.param.representationOpacity, })); if (transparencyLayers.length > 0) { addedRepr.apply(transforms_1.StateTransforms.Representation.TransparencyStructureRepresentation3DFromBundle, { layers: transparencyLayers }, { tags: helpers_1.Tags.Transparency }); } yield update.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!; use negative index to count backwards) 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 `keepOpacity`, current residue opacity 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); } if (!(options === null || options === void 0 ? void 0 : options.keepOpacity)) { 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_transparency_1.clearStructureTransparency)(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]; } } } }), /** Color the parts of sequence in the sequence panel defined by `data`. Color the rest of the sequence in `nonSelectedColor` if provided. * If `structureId` or `structureNumber` is provided, apply to the specified structure (`structureNumber` numbered from 1!; use negative index to count backwards); otherwise apply to all loaded structures. * Remove any previously added coloring, unless `keepColors` is set. */ sequenceColor: (params) => tslib_1.__awaiter(this, void 0, void 0, function* () { var _a, _b; const structureNumberOrId = (_a = params.structureId) !== null && _a !== void 0 ? _a : params.structureNumber; const update = this.plugin.build(); for (const struct of this.getStructures(structureNumberOrId)) { const propsCell = (_b = struct.structureRef.properties) === null || _b === void 0 ? void 0 : _b.cell; if (!propsCell) { console.warn(`Custom structure properties not found (structure node ref "${struct.structureRef.cell.transform.ref}")`); continue; } if (propsCell.transform.transformer.id !== model_1.CustomStructureProperties.id) { console.warn(`Custom structure properties node is of wrong type "${propsCell.transform.transformer.id}"`); continue; } const newColors = []; if (params.nonSelectedColor) { newColors.push({ selector: { name: 'static', params: 'all' }, color: (0, helpers_1.normalizeColor)(params.nonSelectedColor), }); } for (const selection of this.getSelections(params.data, struct.number)) { if (!selection.param.color) continue; newColors.push({ selector: { name: 'bundle', params: selection.bundle }, color: (0, helpers_1.normalizeColor)(selection.param.color), }); } update.to(propsCell).update(model_1.CustomStructureProperties, old => { var _a, _b, _c, _d, _e, _f; const annotProps = { colors: newColors }; const oldColors = (_b = (_a = old.properties) === null || _a === void 0 ? void 0 : _a[sequence_color_annotations_prop_1.SequenceColorAnnotationsProperty.Name]) === null || _b === void 0 ? void 0 : _b.colors; if (params.keepColors && !params.nonSelectedColor && oldColors) { annotProps.colors = oldColors.concat(newColors); } let themeProps; if (params.theme) { themeProps = { useTheme: true, theme: { name: params.theme.name, params: (_c = params.theme.params) !== null && _c !== void 0 ? _c : {} }, themeStrength: (_d = params.theme.themeStrength) !== null && _d !== void 0 ? _d : 1, dilutionColor: (0, helpers_1.normalizeColor)(params.theme.dilutionColor, names_1.ColorNames.white), }; } else if (params.keepColors) { themeProps = (_f = (_e = old.properties) === null || _e === void 0 ? void 0 : _e[sequence_color_theme_prop_1.SequenceColorThemeProperty.Name]) !== null && _f !== void 0 ? _f : { useTheme: false }; } else { themeProps = { useTheme: false }; } return Object.assign(Object.assign({}, old), { properties: Object.assign(Object.assign({}, old.properties), { [sequence_color_annotations_prop_1.SequenceColorAnnotationsProperty.Name]: annotProps, [sequence_color_theme_prop_1.SequenceColorThemeProperty.Name]: themeProps }) }); }); } yield update.commit(); }), /** Remove any sequence coloring previously added by the `sequenceColor` method. * `structureNumberOrId` is either index (numbered from 1!; use negative index to count backwards) or the ID that was provided when loading the structure; * if not provided, will apply to all loaded structures. */ clearSequenceColor: (structureNumberOrId) => tslib_1.__awaiter(this, void 0, void 0, function* () { if (typeof structureNumberOrId === 'number') yield this.visual.sequenceColor({ data: [], structureNumber: structureNumberOrId }); else yield this.visual.sequenceColor({ data: [], structureId: structureNumberOrId }); }), /** 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); (0, plugin_custom_state_1.PluginCustomState)(this.plugin).initParams = this.initParams; // Show/hide buttons in the viewport control panel const hideButtons = this.initParams.hideCanvasControls; this.plugin.config.set(config_1.PluginConfig.Viewport.ShowReset, !hideButtons.includes('all') && !hideButtons.includes('reset')); this.plugin.config.set(config_1.PluginConfig.Viewport.ShowScreenshotControls, !hideButtons.includes('all') && !hideButtons.includes('screenshot')); this.plugin.config.set(config_1.PluginConfig.Viewport.ShowControls, !hideButtons.includes('all') && !hideButtons.includes('controlToggle')); this.plugin.config.set(config_1.PluginConfig.Viewport.ShowExpand, !hideButtons.includes('all') && !hideButtons.includes('expand')); this.plugin.config.set(config_1.PluginConfig.Viewport.ShowSettings, !hideButtons.includes('all') && !hideButtons.includes('controlInfo')); this.plugin.config.set(config_1.PluginConfig.Viewport.ShowSelectionMode, !hideButtons.includes('all') && !hideButtons.includes('selection')); this.plugin.config.set(config_1.PluginConfig.Viewport.ShowAnimation, !hideButtons.includes('all') && !hideButtons.includes('animation')); this.plugin.config.set(config_1.PluginConfig.Viewport.ShowTrajectoryControls, !hideButtons.includes('all') && !hideButtons.includes('trajectory')); // Update layout commands_1.PluginCommands.Layout.Update(this.plugin, { state: (0, helpers_1.pluginLayoutStateFromInitParams)(this.initParams) }); // Set background colour if (this.initParams.bgColor || this.initParams.lighting) { yield this.canvas.applySettings({ color: this.initParams.bgColor, lighting: this.initParams.lighting }); } // Set default highlight and selection colors if (this.initParams.highlightColor || this.initParams.selectColor) { this.visual.setColor({ highlight: this.initParams.highlightColor, select: this.initParams.selectColor }); } // Set selection granularity if (this.initParams.granularity) { this.plugin.managers.interactivity.setProps({ granularity: this.initParams.granularity }); } // 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); } else { if (fullLoad) yield this.clear(); } 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.behaviors.push(spec_1.PluginSpec.Behavior(behavior_6.SequenceColor)); 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)); } const hideButtons = this.initParams.hideCanvasControls; if (hideButtons.includes('all') || hideButtons.includes('reset')) pdbePluginSpec.config.push([config_1.PluginConfig.Viewport.ShowReset, false]); if (hideButtons.includes('all') || hideButtons.includes('screenshot')) pdbePluginSpec.config.push([config_1.PluginConfig.Viewport.ShowScreenshotControls, false]); if (hideButtons.includes('all') || hideButtons.includes('controlToggle')) pdbePluginSpec.config.push([config_1.PluginConfig.Viewport.ShowControls, false]); if (hideButtons.includes('all') || hideButtons.includes('expand')) pdbePluginSpec.config.push([config_1.PluginConfig.Viewport.ShowExpand, false]); if (hideButtons.includes('all') || hideButtons.includes('controlInfo')) pdbePluginSpec.config.push([config_1.PluginConfig.Viewport.ShowSettings, false]); if (hideButtons.includes('all') || hideButtons.includes('selection')) pdbePluginSpec.config.push([config_1.PluginConfig.Viewport.ShowSelectionMode, false]); if (hideButtons.includes('all') || hideButtons.includes('animation')) pdbePluginSpec.config.push([config_1.PluginConfig.Viewport.ShowAnimation, false]); if (hideButtons.includes('all') || hideButtons.includes('trajectory')) pdbePluginSpec.config.push([config_1.PluginConfig.Viewport.ShowTrajectoryControls, false]); pdbePluginSpec.config.push([config_1.PluginConfig.Viewport.ShowToggleFullscreen, false]); pdbePluginSpec.config.push([config_1.PluginConfig.Viewport.ShowIllumination, false]); pdbePluginSpec.config.push([config_1.PluginConfig.Viewport.ShowXR, 'never']); // 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)({