molstar
Version:
A comprehensive macromolecular library.
411 lines (410 loc) • 22.1 kB
JavaScript
"use strict";
/**
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnitsRepresentation = void 0;
var tslib_1 = require("tslib");
var param_definition_1 = require("../../mol-util/param-definition");
var representation_1 = require("./representation");
var representation_2 = require("../representation");
var structure_1 = require("../../mol-model/structure");
var rxjs_1 = require("rxjs");
var render_object_1 = require("../../mol-gl/render-object");
var theme_1 = require("../../mol-theme/theme");
var mol_task_1 = require("../../mol-task");
var loci_1 = require("../../mol-model/loci");
var marker_action_1 = require("../../mol-util/marker-action");
var overpaint_1 = require("../../mol-theme/overpaint");
var transparency_1 = require("../../mol-theme/transparency");
var linear_algebra_1 = require("../../mol-math/linear-algebra");
var int_1 = require("../../mol-data/int");
var clipping_1 = require("../../mol-theme/clipping");
var substance_1 = require("../../mol-theme/substance");
function UnitsRepresentation(label, ctx, getParams, visualCtor) {
var version = 0;
var webgl = ctx.webgl;
var updated = new rxjs_1.Subject();
var materialId = (0, render_object_1.getNextMaterialId)();
var renderObjects = [];
var geometryState = new representation_2.Representation.GeometryState();
var _state = representation_1.StructureRepresentationStateBuilder.create();
var visuals = new Map();
var _structure;
var _groups;
var _params;
var _props;
var _theme = theme_1.Theme.createEmpty();
function createOrUpdate(props, structure) {
var _this = this;
if (props === void 0) { props = {}; }
if (structure && structure !== _structure) {
_params = getParams(ctx, structure);
if (!_props)
_props = param_definition_1.ParamDefinition.getDefaultValues(_params);
}
_props = Object.assign({}, _props, props);
return mol_task_1.Task.create('Creating or updating UnitsRepresentation', function (runtime) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var i, group, visual, promise, oldVisuals, i, group, visualGroup, visual, promise, promise, arr, visual, promise, i, group, visualGroup, visual, promise, promise, visualsList_1, i, il, _a, visual, group, promise, promise;
var _b, _c, _d;
return tslib_1.__generator(this, function (_e) {
switch (_e.label) {
case 0:
if (!(!_structure && !structure)) return [3 /*break*/, 1];
throw new Error('missing structure');
case 1:
if (!(structure && !_structure)) return [3 /*break*/, 8];
// console.log(label, 'initial structure');
// First call with a structure, create visuals for each group.
_groups = structure.unitSymmetryGroups;
i = 0;
_e.label = 2;
case 2:
if (!(i < _groups.length)) return [3 /*break*/, 7];
group = _groups[i];
visual = visualCtor(materialId, structure, _props, webgl);
promise = visual.createOrUpdate({ webgl: webgl, runtime: runtime }, _theme, _props, { group: group, structure: structure });
if (!promise) return [3 /*break*/, 4];
return [4 /*yield*/, promise];
case 3:
_e.sent();
_e.label = 4;
case 4:
setVisualState(visual, group, _state); // current state for new visual
visuals.set(group.hashCode, { visual: visual, group: group });
if (!runtime.shouldUpdate) return [3 /*break*/, 6];
return [4 /*yield*/, runtime.update({ message: 'Creating or updating UnitsVisual', current: i, max: _groups.length })];
case 5:
_e.sent();
_e.label = 6;
case 6:
i++;
return [3 /*break*/, 2];
case 7: return [3 /*break*/, 43];
case 8:
if (!(structure && (!structure_1.Structure.areUnitIdsAndIndicesEqual(structure, _structure) || structure.child !== _structure.child))) return [3 /*break*/, 22];
// console.log(label, 'structures not equivalent');
// Tries to re-use existing visuals for the groups of the new structure.
// Creates additional visuals if needed, destroys left-over visuals.
_groups = structure.unitSymmetryGroups;
oldVisuals = visuals;
visuals = new Map();
i = 0;
_e.label = 9;
case 9:
if (!(i < _groups.length)) return [3 /*break*/, 21];
group = _groups[i];
visualGroup = oldVisuals.get(group.hashCode);
if (!visualGroup) return [3 /*break*/, 15];
visual = visualGroup.visual;
if (!((_b = visual.mustRecreate) === null || _b === void 0 ? void 0 : _b.call(visual, { group: group, structure: structure }, _props, webgl))) return [3 /*break*/, 12];
visual.destroy();
visual = visualCtor(materialId, structure, _props, webgl);
promise = visual.createOrUpdate({ webgl: webgl, runtime: runtime }, _theme, _props, { group: group, structure: structure });
if (!promise) return [3 /*break*/, 11];
return [4 /*yield*/, promise];
case 10:
_e.sent();
_e.label = 11;
case 11:
setVisualState(visual, group, _state); // current state for new visual
return [3 /*break*/, 14];
case 12:
promise = visual.createOrUpdate({ webgl: webgl, runtime: runtime }, _theme, _props, { group: group, structure: structure });
if (!promise) return [3 /*break*/, 14];
return [4 /*yield*/, promise];
case 13:
_e.sent();
_e.label = 14;
case 14:
visuals.set(group.hashCode, { visual: visual, group: group });
oldVisuals.delete(group.hashCode);
// Remove highlight
// TODO: remove selection too??
if (visual.renderObject) {
arr = visual.renderObject.values.tMarker.ref.value.array;
(0, marker_action_1.applyMarkerAction)(arr, int_1.Interval.ofBounds(0, arr.length), marker_action_1.MarkerAction.RemoveHighlight);
}
return [3 /*break*/, 18];
case 15:
visual = visualCtor(materialId, structure, _props, webgl);
promise = visual.createOrUpdate({ webgl: webgl, runtime: runtime }, _theme, _props, { group: group, structure: structure });
if (!promise) return [3 /*break*/, 17];
return [4 /*yield*/, promise];
case 16:
_e.sent();
_e.label = 17;
case 17:
setVisualState(visual, group, _state); // current state for new visual
visuals.set(group.hashCode, { visual: visual, group: group });
_e.label = 18;
case 18:
if (!runtime.shouldUpdate) return [3 /*break*/, 20];
return [4 /*yield*/, runtime.update({ message: 'Creating or updating UnitsVisual', current: i, max: _groups.length })];
case 19:
_e.sent();
_e.label = 20;
case 20:
i++;
return [3 /*break*/, 9];
case 21:
oldVisuals.forEach(function (_a) {
var visual = _a.visual;
// console.log(label, 'removed unused visual');
visual.destroy();
});
return [3 /*break*/, 43];
case 22:
if (!(structure && structure !== _structure && structure_1.Structure.areUnitIdsAndIndicesEqual(structure, _structure))) return [3 /*break*/, 34];
// console.log(label, 'structures equivalent but not identical');
// Expects that for structures with the same hashCode,
// the unitSymmetryGroups are the same as well.
// Re-uses existing visuals for the groups of the new structure.
_groups = structure.unitSymmetryGroups;
i = 0;
_e.label = 23;
case 23:
if (!(i < _groups.length)) return [3 /*break*/, 33];
group = _groups[i];
visualGroup = visuals.get(group.hashCode);
if (!visualGroup) return [3 /*break*/, 29];
visual = visualGroup.visual;
if (!((_c = visual.mustRecreate) === null || _c === void 0 ? void 0 : _c.call(visual, { group: group, structure: structure }, _props, ctx.webgl))) return [3 /*break*/, 26];
visual.destroy();
visual = visualCtor(materialId, structure, _props, ctx.webgl);
visualGroup.visual = visual;
promise = visual.createOrUpdate({ webgl: webgl, runtime: runtime }, _theme, _props, { group: group, structure: structure });
if (!promise) return [3 /*break*/, 25];
return [4 /*yield*/, promise];
case 24:
_e.sent();
_e.label = 25;
case 25:
setVisualState(visual, group, _state); // current state for new visual
return [3 /*break*/, 28];
case 26:
promise = visual.createOrUpdate({ webgl: webgl, runtime: runtime }, _theme, _props, { group: group, structure: structure });
if (!promise) return [3 /*break*/, 28];
return [4 /*yield*/, promise];
case 27:
_e.sent();
_e.label = 28;
case 28:
visualGroup.group = group;
return [3 /*break*/, 30];
case 29: throw new Error("expected to find visual for hashCode ".concat(group.hashCode));
case 30:
if (!runtime.shouldUpdate) return [3 /*break*/, 32];
return [4 /*yield*/, runtime.update({ message: 'Creating or updating UnitsVisual', current: i, max: _groups.length })];
case 31:
_e.sent();
_e.label = 32;
case 32:
i++;
return [3 /*break*/, 23];
case 33: return [3 /*break*/, 43];
case 34:
visualsList_1 = [];
visuals.forEach(function (vg) { return visualsList_1.push(vg); });
i = 0, il = visualsList_1.length;
_e.label = 35;
case 35:
if (!(i < il)) return [3 /*break*/, 43];
_a = visualsList_1[i], visual = _a.visual, group = _a.group;
if (!((_d = visual.mustRecreate) === null || _d === void 0 ? void 0 : _d.call(visual, { group: group, structure: _structure }, _props, ctx.webgl))) return [3 /*break*/, 38];
visual.destroy();
visual = visualCtor(materialId, _structure, _props, webgl);
visualsList_1[i].visual = visual;
promise = visual.createOrUpdate({ webgl: webgl, runtime: runtime }, _theme, _props, { group: group, structure: _structure });
if (!promise) return [3 /*break*/, 37];
return [4 /*yield*/, promise];
case 36:
_e.sent();
_e.label = 37;
case 37:
setVisualState(visual, group, _state); // current state for new visual
return [3 /*break*/, 40];
case 38:
promise = visual.createOrUpdate({ webgl: webgl, runtime: runtime }, _theme, _props);
if (!promise) return [3 /*break*/, 40];
return [4 /*yield*/, promise];
case 39:
_e.sent();
_e.label = 40;
case 40:
if (!runtime.shouldUpdate) return [3 /*break*/, 42];
return [4 /*yield*/, runtime.update({ message: 'Creating or updating UnitsVisual', current: i, max: il })];
case 41:
_e.sent();
_e.label = 42;
case 42:
++i;
return [3 /*break*/, 35];
case 43:
// update list of renderObjects
renderObjects.length = 0;
visuals.forEach(function (_a) {
var visual = _a.visual;
if (visual.renderObject) {
renderObjects.push(visual.renderObject);
geometryState.add(visual.renderObject.id, visual.geometryVersion);
}
});
geometryState.snapshot();
// set new structure
if (structure)
_structure = structure;
// increment version
updated.next(version++);
return [2 /*return*/];
}
});
}); });
}
function getLoci(pickingId) {
var loci = loci_1.EmptyLoci;
visuals.forEach(function (_a) {
var visual = _a.visual;
var _loci = visual.getLoci(pickingId);
if (!(0, loci_1.isEmptyLoci)(_loci))
loci = _loci;
});
return loci;
}
function getAllLoci() {
return [structure_1.Structure.Loci(_structure.target)];
}
function mark(loci, action) {
if (!_structure)
return false;
if (!marker_action_1.MarkerActions.is(_state.markerActions, action))
return false;
if (structure_1.Structure.isLoci(loci) || structure_1.StructureElement.Loci.is(loci) || structure_1.Bond.isLoci(loci)) {
if (!structure_1.Structure.areRootsEquivalent(loci.structure, _structure))
return false;
// Remap `loci` from equivalent structure to the current `_structure`
loci = loci_1.Loci.remap(loci, _structure);
if (structure_1.Structure.isLoci(loci) || (structure_1.StructureElement.Loci.is(loci) && structure_1.StructureElement.Loci.isWholeStructure(loci))) {
// Change to `EveryLoci` to allow for downstream optimizations
loci = loci_1.EveryLoci;
}
}
else if (!(0, loci_1.isEveryLoci)(loci) && !(0, loci_1.isDataLoci)(loci)) {
return false;
}
if (loci_1.Loci.isEmpty(loci))
return false;
var changed = false;
visuals.forEach(function (_a) {
var visual = _a.visual;
changed = visual.mark(loci, action) || changed;
});
return changed;
}
function setVisualState(visual, group, state) {
var visible = state.visible, alphaFactor = state.alphaFactor, pickable = state.pickable, overpaint = state.overpaint, transparency = state.transparency, substance = state.substance, clipping = state.clipping, transform = state.transform, unitTransforms = state.unitTransforms;
if (visible !== undefined)
visual.setVisibility(visible);
if (alphaFactor !== undefined)
visual.setAlphaFactor(alphaFactor);
if (pickable !== undefined)
visual.setPickable(pickable);
if (overpaint !== undefined)
visual.setOverpaint(overpaint, webgl);
if (transparency !== undefined)
visual.setTransparency(transparency, webgl);
if (substance !== undefined)
visual.setSubstance(substance, webgl);
if (clipping !== undefined)
visual.setClipping(clipping);
if (transform !== undefined)
visual.setTransform(transform);
if (unitTransforms !== undefined) {
if (unitTransforms) {
// console.log(group.hashCode, unitTransforms.getSymmetryGroupTransforms(group))
visual.setTransform(undefined, unitTransforms.getSymmetryGroupTransforms(group));
}
else {
visual.setTransform(undefined, null);
}
}
}
function setState(state) {
var visible = state.visible, alphaFactor = state.alphaFactor, pickable = state.pickable, overpaint = state.overpaint, transparency = state.transparency, substance = state.substance, clipping = state.clipping, transform = state.transform, unitTransforms = state.unitTransforms, syncManually = state.syncManually, markerActions = state.markerActions;
var newState = {};
if (visible !== _state.visible)
newState.visible = visible;
if (alphaFactor !== _state.alphaFactor)
newState.alphaFactor = alphaFactor;
if (pickable !== _state.pickable)
newState.pickable = pickable;
if (overpaint !== undefined && _structure) {
newState.overpaint = overpaint_1.Overpaint.remap(overpaint, _structure);
}
if (transparency !== undefined && _structure) {
newState.transparency = transparency_1.Transparency.remap(transparency, _structure);
}
if (substance !== undefined && _structure) {
newState.substance = substance_1.Substance.remap(substance, _structure);
}
if (clipping !== undefined && _structure) {
newState.clipping = clipping_1.Clipping.remap(clipping, _structure);
}
if (transform !== undefined && !linear_algebra_1.Mat4.areEqual(transform, _state.transform, linear_algebra_1.EPSILON)) {
newState.transform = transform;
}
if (unitTransforms !== _state.unitTransforms || (unitTransforms === null || unitTransforms === void 0 ? void 0 : unitTransforms.version) !== state.unitTransformsVersion) {
newState.unitTransforms = unitTransforms;
_state.unitTransformsVersion = unitTransforms ? unitTransforms === null || unitTransforms === void 0 ? void 0 : unitTransforms.version : -1;
}
if (syncManually !== _state.syncManually)
newState.syncManually = syncManually;
if (markerActions !== _state.markerActions)
newState.markerActions = markerActions;
visuals.forEach(function (_a) {
var visual = _a.visual, group = _a.group;
return setVisualState(visual, group, newState);
});
representation_1.StructureRepresentationStateBuilder.update(_state, newState);
}
function setTheme(theme) {
_theme = theme;
}
function destroy() {
visuals.forEach(function (_a) {
var visual = _a.visual;
return visual.destroy();
});
visuals.clear();
}
return {
label: label,
get groupCount() {
var groupCount = 0;
visuals.forEach(function (_a) {
var visual = _a.visual;
if (visual.renderObject)
groupCount += visual.groupCount;
});
return groupCount;
},
get geometryVersion() { return geometryState.version; },
get props() { return _props; },
get params() { return _params; },
get state() { return _state; },
get theme() { return _theme; },
renderObjects: renderObjects,
updated: updated,
createOrUpdate: createOrUpdate,
setState: setState,
setTheme: setTheme,
getLoci: getLoci,
getAllLoci: getAllLoci,
mark: mark,
destroy: destroy
};
}
exports.UnitsRepresentation = UnitsRepresentation;