UNPKG

@rcsb/rcsb-saguaro-3d

Version:
235 lines (234 loc) 12.4 kB
"use strict"; /* * Copyright (c) 2021 RCSB PDB and contributors, licensed under MIT, See LICENSE file for more info. * @author Joan Segura Mora <joan.segura@rcsb.org> */ Object.defineProperty(exports, "__esModule", { value: true }); exports.MsaBehaviourObserver = void 0; const tslib_1 = require("tslib"); const rxjs_1 = require("rxjs"); const selection_1 = require("@rcsb/rcsb-molstar/build/src/viewer/helpers/selection"); const FunctionCall_1 = require("../../Utils/FunctionCall"); var onetimeCall = FunctionCall_1.FunctionCall.onetimeCall; const TagDelimiter_1 = require("@rcsb/rcsb-api-tools/lib/RcsbUtils/TagDelimiter"); class MsaBehaviourObserver { constructor(structureLoader) { this.structureLoader = structureLoader; } observe(structureViewer, stateManager) { this.structureBehaviour = new MsaBehaviour(structureViewer, stateManager, this.structureLoader); } unsubscribe() { this.structureBehaviour.unsubscribe(); } } exports.MsaBehaviourObserver = MsaBehaviourObserver; class MsaBehaviour { constructor(structureViewer, stateManager, structureLoader) { this.componentList = []; this.CREATE_COMPONENT_THR = 5; this.structureViewer = structureViewer; this.stateManager = stateManager; this.structureLoader = structureLoader; this.subscription = this.subscribe(); } subscribe() { return this.stateManager.subscribe((o) => tslib_1.__awaiter(this, void 0, void 0, function* () { if (o.type == "model-change" && o.view == "1d-view" && o.data) yield this.modelChange(o.data); if (o.type == "representation-change" && o.view == "1d-view" && o.data) this.reprChange(o.data); if (o.type == "selection-change" && o.view == "1d-view") this.selectionChange(); if (o.type == "hover-change" && o.view == "1d-view") this.hoverChange(); if (o.type == "feature-click" && o.view == "1d-view" && o.data) yield this.featureClick(o.data); if (o.type == "selection-change" && o.view == "3d-view") yield this.isSelectionEmpty(); if (o.type == "structure-download" && o.view == "ui-view") this.downloadStructures(); if (o.type == "component-info" && o.view == "1d-view" && o.data) this.componentInfo(o.data); })); } featureClick(data) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const cameraFocus = onetimeCall((d) => { const { modelId, labelAsymId, region, operatorName } = d; const regions = [region]; const residues = regions.map(r => r.begin == r.end ? [r.begin] : [r.begin, r.end]).flat().filter(r => r != null); this.structureViewer.cameraFocus(modelId, labelAsymId, residues, operatorName); }); yield this.removeComponent(); if (!data || data.length == 0) this.resetPluginView(); const numRes = data === null || data === void 0 ? void 0 : data.map(d => (d.region.end - d.region.begin + 1)).reduce((prev, curr) => prev + curr, 0); if (!numRes) return; data === null || data === void 0 ? void 0 : data.forEach(d => { const { modelId, labelAsymId, region, operatorName } = d; const regions = [region]; if (modelId && labelAsymId && Array.isArray(regions) && regions.length > 0) { const residues = regions.map(r => r.begin == r.end ? [r.begin] : [r.begin, r.end]).flat().filter(r => r != null); if (residues.length == 0) return; if (numRes == (data === null || data === void 0 ? void 0 : data.length)) this.structureViewer.setFocus(modelId, labelAsymId, residues[0], residues[0], operatorName); cameraFocus(d); const ranges = regions.map(r => ({ modelId, labelAsymId, begin: r.begin, end: r.end, operatorName })); if ((data === null || data === void 0 ? void 0 : data.map(d => (d.region.end - d.region.begin + 1) < this.CREATE_COMPONENT_THR ? 1 : 0).reduce((prev, curr) => prev + curr, 0)) == (data === null || data === void 0 ? void 0 : data.length)) rxjs_1.asyncScheduler.schedule(() => tslib_1.__awaiter(this, void 0, void 0, function* () { const x = residues[0]; const y = residues[residues.length - 1]; const selectedComponentId = `${modelId}${TagDelimiter_1.TagDelimiter.instance}${labelAsymId + ":" + ((x === y) ? x.toString() : x.toString() + "," + y.toString())}`; yield this.structureViewer.createComponent(selectedComponentId, ranges, "ball-and-stick"); this.componentList.push(selectedComponentId); })); } else { this.structureViewer.clearSelection("select", { modelId, labelAsymId }); } }); }); } hoverChange() { this.select("hover"); } selectionChange() { this.select("select"); } unsubscribe() { this.subscription.unsubscribe(); } reprChange(data) { var _a, _b; if (data) { const chainInfo = (_a = this.stateManager.assemblyModelSate.getModelChainInfo(this.getRcsbId(data.pdb))) === null || _a === void 0 ? void 0 : _a.chains.find(ch => (("entityId" in data.pdb && ch.entityId == data.pdb.entityId) || ("instanceId" in data.pdb && ch.label == data.pdb.instanceId))); if (!chainInfo) return; switch (data.tag) { case "aligned": const asymId = chainInfo.label; const operatorInfo = (_b = chainInfo.operators) !== null && _b !== void 0 ? _b : []; const alignedCompId = `${data.pdb.entryId}${TagDelimiter_1.TagDelimiter.entity}${chainInfo.entityId}${TagDelimiter_1.TagDelimiter.instance}${asymId}${TagDelimiter_1.TagDelimiter.assembly}${operatorInfo[0].ids.join(",")}${TagDelimiter_1.TagDelimiter.assembly}${"polymer"}`; this.structureViewer.displayComponent(alignedCompId, !data.isHidden); break; case "polymer": const polymerCompId = `${data.pdb.entryId}${TagDelimiter_1.TagDelimiter.entity}${chainInfo.entityId}${TagDelimiter_1.TagDelimiter.assembly}${data.tag}`; this.structureViewer.displayComponent(polymerCompId, !data.isHidden); break; case "non-polymer": (0, selection_1.createSelectionExpressions)(data.pdb.entryId).map(expression => expression.tag).filter(tag => (tag != "water" && tag != "polymer")).forEach(tag => { const nonPolymerCompId = `${data.pdb.entryId}${TagDelimiter_1.TagDelimiter.entity}${chainInfo.entityId}${TagDelimiter_1.TagDelimiter.assembly}${tag}`; this.structureViewer.displayComponent(nonPolymerCompId, !data.isHidden); }); break; } } } modelChange(data) { return tslib_1.__awaiter(this, void 0, void 0, function* () { if (data) { yield this.structureLoader.load(this.structureViewer, data.pdb, data.targetAlignment); this.stateManager.next({ type: "model-ready", view: "3d-view", data }); } }); } select(mode) { if (mode == "select") this.structureViewer.clearFocus(); if (this.stateManager.selectionState.getSelection(mode).length == 0) this.structureViewer.clearSelection(mode); this.structureViewer.select(this.stateManager.selectionState.getSelection(mode).map(selectedRegion => { return selectedRegion.regions.map(region => { return { modelId: selectedRegion.modelId, labelAsymId: selectedRegion.labelAsymId, operatorName: selectedRegion.operatorName, begin: region.begin, end: region.end }; }); }).flat(), mode, "set"); } resetPluginView() { this.structureViewer.clearFocus(); this.structureViewer.resetCamera(); } isSelectionEmpty() { return tslib_1.__awaiter(this, void 0, void 0, function* () { if (this.stateManager.selectionState.getLastSelection() == null) { yield this.removeComponent(); this.resetPluginView(); } }); } removeComponent() { return tslib_1.__awaiter(this, void 0, void 0, function* () { yield Promise.all(this.componentList.map((compId) => tslib_1.__awaiter(this, void 0, void 0, function* () { yield this.structureViewer.removeComponent(compId); }))); }); } getRcsbId(pdb) { if ("instanceId" in pdb) return `${pdb.entryId}${TagDelimiter_1.TagDelimiter.instance}${pdb.instanceId}`; else return `${pdb.entryId}${TagDelimiter_1.TagDelimiter.entity}${pdb.entityId}`; } downloadStructures() { this.structureViewer.exportLoadedStructures().then(() => { console.info("Download structures"); }); } componentInfo(data) { var _a, _b; const chainInfo = (_a = this.stateManager.assemblyModelSate.getModelChainInfo(this.getRcsbId(data.pdb))) === null || _a === void 0 ? void 0 : _a.chains.find(ch => (("entityId" in data.pdb && ch.entityId == data.pdb.entityId) || ("instanceId" in data.pdb && ch.label == data.pdb.instanceId))); if (!chainInfo) return; let isComponent = false; let isVisible = false; switch (data.tag) { case "aligned": const asymId = chainInfo.label; const operatorInfo = (_b = chainInfo.operators) !== null && _b !== void 0 ? _b : []; const alignedCompId = `${data.pdb.entryId}${TagDelimiter_1.TagDelimiter.entity}${chainInfo.entityId}${TagDelimiter_1.TagDelimiter.instance}${asymId}${TagDelimiter_1.TagDelimiter.assembly}${operatorInfo[0].ids.join(",")}${TagDelimiter_1.TagDelimiter.assembly}${"polymer"}`; isComponent = this.structureViewer.isComponent(alignedCompId); isVisible = this.structureViewer.displayComponent(alignedCompId); break; case "polymer": const polymerCompId = `${data.pdb.entryId}${TagDelimiter_1.TagDelimiter.entity}${chainInfo.entityId}${TagDelimiter_1.TagDelimiter.assembly}${data.tag}`; this.structureViewer.displayComponent(polymerCompId); isComponent = this.structureViewer.isComponent(polymerCompId); isVisible = this.structureViewer.displayComponent(polymerCompId); break; case "non-polymer": for (const tag of (0, selection_1.createSelectionExpressions)(data.pdb.entryId).map(expression => expression.tag).filter(tag => (tag != "water" && tag != "polymer"))) { const nonPolymerCompId = `${data.pdb.entryId}${TagDelimiter_1.TagDelimiter.entity}${chainInfo.entityId}${TagDelimiter_1.TagDelimiter.assembly}${tag}`; this.structureViewer.displayComponent(nonPolymerCompId); isComponent = this.structureViewer.isComponent(nonPolymerCompId); isVisible = this.structureViewer.displayComponent(nonPolymerCompId); if (isComponent) break; } break; } this.stateManager.next({ type: "component-info", view: "3d-view", data: Object.assign(Object.assign({}, data), { isComponent, isVisible }) }); } }