@rcsb/rcsb-saguaro-3d
Version:
RCSB Molstar/Saguaro Web App
235 lines (234 loc) • 12.4 kB
JavaScript
;
/*
* 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 })
});
}
}