molstar
Version:
A comprehensive macromolecular library.
915 lines (914 loc) • 61.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EntityNode = exports.GroupNode = exports.EntityControls = exports.FocusInfo = exports.SelectionInfo = exports.ModelInfo = void 0;
exports.MesoMarkdownAnchor = MesoMarkdownAnchor;
exports.MesoViewportSnapshotDescription = MesoViewportSnapshotDescription;
const tslib_1 = require("tslib");
const jsx_runtime_1 = require("react/jsx-runtime");
/**
* Copyright (c) 2022-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
const base_1 = require("../../../mol-plugin-ui/base");
const common_1 = require("../../../mol-plugin-ui/controls/common");
const icons_1 = require("../../../mol-plugin-ui/controls/icons");
const commands_1 = require("../../../mol-plugin/commands");
const mol_state_1 = require("../../../mol-state");
const parameters_1 = require("../../../mol-plugin-ui/controls/parameters");
const param_definition_1 = require("../../../mol-util/param-definition");
const clip_1 = require("../../../mol-util/clip");
const color_1 = require("../../../mol-util/color");
const color_2 = require("../../../mol-plugin-ui/controls/color");
const marker_action_1 = require("../../../mol-util/marker-action");
const loci_1 = require("../../../mol-model/loci");
const mol_util_1 = require("../../../mol-util");
const state_1 = require("../data/state");
const react_1 = tslib_1.__importStar(require("react"));
const element_1 = require("../../../mol-model/structure/structure/element");
const structure_1 = require("../../../mol-model/structure");
const geometry_1 = require("../../../mol-math/geometry");
const camera_1 = require("../behavior/camera");
const react_markdown_1 = tslib_1.__importDefault(require("react-markdown"));
const rxjs_1 = require("rxjs");
const states_1 = require("./states");
function centerLoci(plugin, loci, durationMs = 250) {
const { canvas3d } = plugin;
if (!canvas3d)
return;
const sphere = loci_1.Loci.getBoundingSphere(loci) || (0, geometry_1.Sphere3D)();
const snapshot = canvas3d.camera.getCenter(sphere.center);
canvas3d.requestCameraReset({ durationMs, snapshot });
}
class ModelInfo extends base_1.PluginUIComponent {
constructor() {
super(...arguments);
this.state = {
isDisabled: false,
};
}
componentDidMount() {
this.subscribe(this.plugin.state.data.behaviors.isUpdating, v => {
this.setState({ isDisabled: v });
});
this.subscribe(this.plugin.state.events.cell.stateUpdated, e => {
if (!this.state.isDisabled && state_1.MesoscaleState.has(this.plugin) && state_1.MesoscaleState.ref(this.plugin) === e.ref) {
this.forceUpdate();
}
});
}
get info() {
if (!state_1.MesoscaleState.has(this.plugin))
return;
const state = state_1.MesoscaleState.get(this.plugin);
if (!state.description && !state.link)
return;
return {
selectionDescription: state.focusInfo,
description: state.description,
link: state.link,
};
}
render() {
const info = this.info;
return info && (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsxs)("div", { id: 'modelinfo', className: 'msp-help-text', children: [(0, jsx_runtime_1.jsx)("div", { children: info.description }), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("a", { href: info.link, target: '_blank', children: "Source" }) })] }) });
}
}
exports.ModelInfo = ModelInfo;
const SelectionStyleParam = param_definition_1.ParamDefinition.Select('color+outline', param_definition_1.ParamDefinition.objectToOptions({
'color+outline': 'Color & Outline',
'color': 'Color',
'outline': 'Outline'
}));
class SelectionInfo extends base_1.PluginUIComponent {
constructor() {
super(...arguments);
this.state = {
isDisabled: false,
};
}
componentDidMount() {
this.subscribe(this.plugin.state.data.behaviors.isUpdating, v => {
this.setState({ isDisabled: v });
});
this.subscribe(this.plugin.managers.structure.selection.events.changed, e => {
if (!this.state.isDisabled) {
this.forceUpdate();
}
});
}
get info() {
const infos = [];
this.plugin.managers.structure.selection.entries.forEach((e, k) => {
var _a;
if (element_1.StructureElement.Loci.is(e.selection) && !element_1.StructureElement.Loci.isEmpty(e.selection)) {
const cell = this.plugin.helpers.substructureParent.get(e.selection.structure);
const { entities } = e.selection.structure.model;
const description = entities.data.pdbx_description.value(0)[0] || 'model';
infos.push({
description: description,
label: ((_a = cell === null || cell === void 0 ? void 0 : cell.obj) === null || _a === void 0 ? void 0 : _a.label) || 'Unknown',
key: k,
});
}
});
return infos;
}
find(label) {
state_1.MesoscaleState.set(this.plugin, { filter: `"${label}"` });
if (label)
(0, state_1.expandAllGroups)(this.plugin);
}
;
remove(key) {
const e = this.plugin.managers.structure.selection.entries.get(key);
if (!e)
return;
const loci = structure_1.Structure.toStructureElementLoci(e.selection.structure);
this.plugin.managers.interactivity.lociSelects.deselect({ loci }, false);
}
center(key) {
const e = this.plugin.managers.structure.selection.entries.get(key);
if (!e)
return;
const loci = structure_1.Structure.toStructureElementLoci(e.selection.structure);
centerLoci(this.plugin, loci);
const cell = this.plugin.helpers.substructureParent.get(loci.structure);
const d = (0, state_1.getCellDescription)(cell); // '### ' + cell?.obj?.label + '\n\n' + cell?.obj?.description;
state_1.MesoscaleState.set(this.plugin, { focusInfo: `${d}` });
}
get selection() {
const info = this.info;
const help_selection = (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { children: ["Use ", (0, jsx_runtime_1.jsx)("i", { children: "ctrl+left" }), " to select entities, either on the 3D canvas or in the tree below"] }), (0, jsx_runtime_1.jsxs)("div", { children: ["Use ", (0, jsx_runtime_1.jsx)("i", { children: "shift+left" }), " to select individual chain on the 3D canvas"] })] });
if (!info.length)
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)("div", { id: 'seleinfo', className: 'msp-help-text', children: help_selection }) });
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsxs)("div", { id: 'seleinfo', children: [info.map((entry, index) => {
const label = (0, jsx_runtime_1.jsx)(common_1.Button, { className: `msp-btn-tree-label`, noOverflow: true, disabled: this.state.isDisabled, onClick: () => this.center(entry.key), children: (0, jsx_runtime_1.jsx)("span", { title: entry.label, children: entry.label }) });
const find = (0, jsx_runtime_1.jsx)(common_1.IconButton, { svg: icons_1.SearchSvg, toggleState: false, disabled: this.state.isDisabled, small: true, onClick: () => this.find(entry.label) });
const remove = (0, jsx_runtime_1.jsx)(common_1.IconButton, { svg: icons_1.CloseSvg, toggleState: false, disabled: this.state.isDisabled, onClick: () => this.remove(entry.key) });
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsxs)("div", { className: `msp-flex-row`, style: { margin: `1px 5px 1px ${1 * 10 + 5}px` }, children: [label, find, remove] }, index) });
}), ";"] }) });
}
get style() {
var _a;
const p = (_a = this.plugin.canvas3d) === null || _a === void 0 ? void 0 : _a.props;
if (!p)
return;
if (p.renderer.dimStrength === 1 && p.marking.enabled)
return 'color+outline';
if (p.renderer.dimStrength === 1)
return 'color';
if (p.marking.enabled)
return 'outline';
}
setStyle(value) {
var _a, _b, _c, _d;
if (value.includes('color') && value.includes('outline')) {
(_a = this.plugin.canvas3d) === null || _a === void 0 ? void 0 : _a.setProps({
renderer: {
dimStrength: 1,
},
marking: {
enabled: true
}
});
}
else if (value.includes('color')) {
(_b = this.plugin.canvas3d) === null || _b === void 0 ? void 0 : _b.setProps({
renderer: {
dimStrength: 1,
},
marking: {
enabled: false
}
});
}
else if (value.includes('outline')) {
(_c = this.plugin.canvas3d) === null || _c === void 0 ? void 0 : _c.setProps({
renderer: {
dimStrength: 0,
selectStrength: 0.3,
},
marking: {
enabled: true
}
});
}
else {
(_d = this.plugin.canvas3d) === null || _d === void 0 ? void 0 : _d.setProps({
renderer: {
dimStrength: 0,
selectStrength: 0,
},
marking: {
enabled: false
}
});
}
this.forceUpdate();
}
renderStyle() {
const style = this.style || '';
return (0, jsx_runtime_1.jsx)("div", { id: 'selestyle', style: { margin: '5px', marginBottom: '10px' }, children: (0, jsx_runtime_1.jsx)(parameters_1.SelectControl, { name: 'Style', param: SelectionStyleParam, value: style, onChange: (e) => { this.setStyle(e.value); } }) });
}
render() {
return (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [this.renderStyle(), this.selection] });
}
}
exports.SelectionInfo = SelectionInfo;
function MesoMarkdownAnchor({ href, children, element }) {
const plugin = react_1.default.useContext(base_1.PluginReactContext);
if (!href)
return element;
// Decode the href to handle encoded spaces and other characters
const decodedHref = href ? decodeURIComponent(href) : '';
const handleHover = (e) => {
var _a, _b, _c, _d, _e, _f;
e.preventDefault();
if (decodedHref.startsWith('i')) {
(_a = plugin.canvas3d) === null || _a === void 0 ? void 0 : _a.mark({ loci: loci_1.EveryLoci }, marker_action_1.MarkerAction.RemoveHighlight);
const query_names = decodedHref.substring(1).split(',');
for (const query_name of query_names) {
const entities = (0, state_1.getEveryEntity)(plugin, query_name);
for (const r of entities) {
const repr = (_b = r.obj) === null || _b === void 0 ? void 0 : _b.data.repr;
if (repr) {
(_c = plugin.canvas3d) === null || _c === void 0 ? void 0 : _c.mark({ repr, loci: loci_1.EveryLoci }, marker_action_1.MarkerAction.Highlight);
}
}
}
}
else if (decodedHref.startsWith('g')) {
(_d = plugin.canvas3d) === null || _d === void 0 ? void 0 : _d.mark({ loci: loci_1.EveryLoci }, marker_action_1.MarkerAction.RemoveHighlight);
const qindex = decodedHref.indexOf('.');
const query = decodedHref.substring(1, qindex) + ':';
const query_names = decodedHref.substring(qindex + 1).split(',');
for (const query_name of query_names) {
const e = (0, state_1.getAllEntities)(plugin, query + query_name);
for (const r of e) {
const repr = (_e = r.obj) === null || _e === void 0 ? void 0 : _e.data.repr;
if (repr) {
(_f = plugin.canvas3d) === null || _f === void 0 ? void 0 : _f.mark({ repr, loci: loci_1.EveryLoci }, marker_action_1.MarkerAction.Highlight);
}
}
}
}
};
const handleLeave = (e) => {
var _a;
// Implement your hover off logic here
// Example: Perform an action if the href starts with 'h'
if (decodedHref.startsWith('i') || decodedHref.startsWith('g')) {
// Example hover off action
e.preventDefault();
(_a = plugin.canvas3d) === null || _a === void 0 ? void 0 : _a.mark({ loci: loci_1.EveryLoci }, marker_action_1.MarkerAction.RemoveHighlight);
}
};
const handleClick = (e) => {
var _a, _b, _c, _d, _e, _f, _g, _h;
e.preventDefault();
if (href.startsWith('#')) {
plugin.managers.snapshot.applyKey(decodedHref.substring(1));
}
else if (decodedHref.startsWith('i')) {
plugin.managers.interactivity.lociSelects.deselectAll();
(_a = plugin.canvas3d) === null || _a === void 0 ? void 0 : _a.mark({ loci: loci_1.EveryLoci }, marker_action_1.MarkerAction.RemoveHighlight);
const query_names = decodedHref.substring(1).split(',');
for (const query_name of query_names) {
const entities = (0, state_1.getFilteredEntities)(plugin, '', query_name);
for (const r of entities) {
const repr = (_b = r.obj) === null || _b === void 0 ? void 0 : _b.data.repr;
if (repr) {
(_c = plugin.canvas3d) === null || _c === void 0 ? void 0 : _c.mark({ repr, loci: loci_1.EveryLoci }, marker_action_1.MarkerAction.Highlight);
}
const cell = r;
if (!(((_d = cell === null || cell === void 0 ? void 0 : cell.obj) === null || _d === void 0 ? void 0 : _d.data.sourceData) instanceof structure_1.Structure)) {
return;
}
const loci = structure_1.Structure.toStructureElementLoci(cell.obj.data.sourceData);
plugin.managers.interactivity.lociSelects.toggle({ loci }, false);
}
}
}
else if (decodedHref.startsWith('g')) {
plugin.managers.interactivity.lociSelects.deselectAll();
(_e = plugin.canvas3d) === null || _e === void 0 ? void 0 : _e.mark({ loci: loci_1.EveryLoci }, marker_action_1.MarkerAction.RemoveHighlight);
const qindex = decodedHref.indexOf('.');
const query = decodedHref.substring(1, qindex) + ':';
const query_names = decodedHref.substring(qindex + 1).split(',');
for (const query_name of query_names) {
const entities = (0, state_1.getAllEntities)(plugin, query + query_name);
for (const r of entities) {
const repr = (_f = r.obj) === null || _f === void 0 ? void 0 : _f.data.repr;
if (repr) {
(_g = plugin.canvas3d) === null || _g === void 0 ? void 0 : _g.mark({ repr, loci: loci_1.EveryLoci }, marker_action_1.MarkerAction.Highlight);
}
const cell = r;
if (!(((_h = cell === null || cell === void 0 ? void 0 : cell.obj) === null || _h === void 0 ? void 0 : _h.data.sourceData) instanceof structure_1.Structure))
return;
const loci = structure_1.Structure.toStructureElementLoci(cell.obj.data.sourceData);
plugin.managers.interactivity.lociSelects.toggle({ loci }, false);
}
}
}
else {
// open the link in a new tab
window.open(decodedHref, '_blank');
}
};
if (decodedHref[0] === '#') {
return (0, jsx_runtime_1.jsx)("a", { href: decodedHref[0], onMouseOver: handleHover, onClick: handleClick, children: children });
}
if (decodedHref[0] === 'i' || decodedHref[0] === 'g') {
return (0, jsx_runtime_1.jsx)("a", { href: decodedHref[0], onMouseLeave: handleLeave, onMouseOver: handleHover, onClick: handleClick, children: children });
}
if (decodedHref[0] === 'h') {
return (0, jsx_runtime_1.jsx)("a", { href: decodedHref[0], onClick: handleClick, rel: 'noopener noreferrer', children: children });
}
return element;
}
function MesoViewportSnapshotDescription() {
var _a;
let tSize = 14;
const plugin = react_1.default.useContext(base_1.PluginReactContext);
if (state_1.MesoscaleState.has(plugin)) {
const state = state_1.MesoscaleState.get(plugin);
tSize = state.textSizeDescription;
}
const [_, setV] = react_1.default.useState(0);
const [isShown, setIsShown] = (0, react_1.useState)(true);
const [textSize, setTextSize] = (0, react_1.useState)(tSize);
const toggleVisibility = () => {
setIsShown(!isShown);
};
const increaseTextSize = () => {
setTextSize(prevSize => Math.min(prevSize + 2, 50)); // Increase the text size by 2px, but not above 50px
};
const decreaseTextSize = () => {
setTextSize(prevSize => Math.max(prevSize - 2, 2)); // Decrease the text size by 2px, but not below 2px
};
react_1.default.useEffect(() => {
const sub = plugin.managers.snapshot.events.changed.subscribe(() => setV(v => v + 1));
return () => sub.unsubscribe();
}, [plugin]);
const current = plugin.managers.snapshot.state.current;
if (!current)
return null;
const e = plugin.managers.snapshot.getEntry(current);
if (!((_a = e === null || e === void 0 ? void 0 : e.description) === null || _a === void 0 ? void 0 : _a.trim()))
return null;
if (state_1.MesoscaleState.has(plugin)) {
state_1.MesoscaleState.set(plugin, { textSizeDescription: textSize });
}
const showInfo = (0, jsx_runtime_1.jsx)(common_1.IconButton, { svg: isShown ? icons_1.TooltipTextSvg : icons_1.TooltipTextOutlineSvg, flex: '20px', onClick: toggleVisibility, title: isShown ? 'Hide Description' : 'Show Description' });
const increasePoliceSize = (0, jsx_runtime_1.jsx)(common_1.IconButton, { svg: icons_1.PlusBoxSvg, flex: '20px', onClick: increaseTextSize, title: 'Bigger Text' });
const decreasePoliceSize = (0, jsx_runtime_1.jsx)(common_1.IconButton, { svg: icons_1.MinusBoxSvg, flex: '20px', onClick: decreaseTextSize, title: 'Smaller Text' });
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { id: 'snapinfoctrl', className: "msp-state-snapshot-viewport-controls", style: { marginRight: '30px' }, children: [showInfo, increasePoliceSize, decreasePoliceSize] }), (0, jsx_runtime_1.jsx)("div", { id: 'snapinfo', className: `msp-snapshot-description-me ${isShown ? 'shown' : 'hidden'}`, style: { fontSize: `${textSize}px` }, children: e.descriptionFormat === 'plaintext'
&& e.description
|| (0, jsx_runtime_1.jsx)(react_markdown_1.default, { skipHtml: false, components: { a: MesoMarkdownAnchor }, children: e.description }) })] }));
}
class FocusInfo extends base_1.PluginUIComponent {
componentDidMount() {
this.subscribe((0, rxjs_1.combineLatest)([
this.plugin.state.data.behaviors.isUpdating,
this.plugin.managers.structure.selection.events.changed
]), ([isUpdating]) => {
if (!isUpdating)
this.forceUpdate();
});
}
get info() {
let focusInfo = '';
if (state_1.MesoscaleState.has(this.plugin)) {
const state = state_1.MesoscaleState.get(this.plugin);
if (state.focusInfo)
focusInfo = state.focusInfo;
}
return focusInfo;
}
render() {
const focusInfo = this.info;
const description = (focusInfo !== '') ? (0, jsx_runtime_1.jsx)(react_markdown_1.default, { skipHtml: true, components: { a: MesoMarkdownAnchor }, children: focusInfo }) : '';
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)("div", { id: 'focusinfo', className: 'msp-help-text', children: description }) });
}
}
exports.FocusInfo = FocusInfo;
class EntityControls extends base_1.PluginUIComponent {
constructor() {
super(...arguments);
this.filterRef = react_1.default.createRef();
this.prevFilter = '';
this.filterFocus = false;
this.state = {
isDisabled: false,
};
this.setGroupBy = (value) => {
this.roots.forEach((c, i) => {
if (c.state.isHidden && value === i || !c.state.isHidden && value !== i) {
commands_1.PluginCommands.State.ToggleVisibility(this.plugin, { state: c.parent, ref: c.transform.ref });
}
});
};
this.setFilter = (value) => {
this.filterFocus = true;
const filter = value.trim().replace(/\s+/gi, ' ');
state_1.MesoscaleState.set(this.plugin, { filter });
if (filter)
(0, state_1.expandAllGroups)(this.plugin);
};
this.setGraphics = (graphics) => {
state_1.MesoscaleState.set(this.plugin, { graphics });
this.plugin.customState.graphicsMode = graphics;
if (graphics === 'custom')
return;
const update = this.plugin.state.data.build();
const { lodLevels, approximate, alphaThickness } = (0, state_1.getGraphicsModeProps)(graphics);
for (const r of (0, state_1.getAllEntities)(this.plugin)) {
update.to(r).update(old => {
if (old.type) {
old.type.params.lodLevels = lodLevels;
old.type.params.approximate = approximate;
old.type.params.alphaThickness = alphaThickness;
}
});
}
for (const g of (0, state_1.getAllGroups)(this.plugin)) {
update.to(g).update(old => {
old.lod.lodLevels = lodLevels;
old.lod.approximate = approximate;
});
}
update.commit();
(0, state_1.setGraphicsCanvas3DProps)(this.plugin, graphics);
};
}
componentDidMount() {
this.subscribe(this.plugin.state.events.object.created, e => {
this.forceUpdate();
});
this.subscribe(this.plugin.state.events.object.removed, e => {
this.forceUpdate();
});
this.subscribe(this.plugin.state.data.behaviors.isUpdating, v => {
this.setState({ isDisabled: v });
});
this.subscribe(this.plugin.state.events.cell.stateUpdated, e => {
if (!this.state.isDisabled && this.roots.some(r => e.cell === r) || (state_1.MesoscaleState.has(this.plugin) && state_1.MesoscaleState.ref(this.plugin) === e.ref)) {
this.forceUpdate();
}
});
}
componentDidUpdate() {
var _a;
const filter = this.filter;
if (this.filterFocus) {
(_a = this.filterRef.current) === null || _a === void 0 ? void 0 : _a.focus();
this.prevFilter = filter;
}
}
get roots() {
return (0, state_1.getRoots)(this.plugin);
}
get groupBy() {
const roots = this.roots;
for (let i = 0, il = roots.length; i < il; ++i) {
if (!roots[i].state.isHidden)
return i;
}
return 0;
}
get filter() {
return state_1.MesoscaleState.has(this.plugin) ? state_1.MesoscaleState.get(this.plugin).filter : '';
}
get graphics() {
const customState = this.plugin.customState;
return state_1.MesoscaleState.has(this.plugin) ? state_1.MesoscaleState.get(this.plugin).graphics : customState.graphicsMode;
}
renderGraphics() {
const graphics = this.graphics;
return (0, jsx_runtime_1.jsx)("div", { id: 'graphicsquality', style: { margin: '5px', marginBottom: '10px' }, children: (0, jsx_runtime_1.jsx)(parameters_1.SelectControl, { name: 'Graphics', param: state_1.MesoscaleStateParams.graphics, value: `${graphics}`, onChange: (e) => { this.setGraphics(e.value); } }) });
}
render() {
const roots = this.roots;
if (roots.length === 0 || !state_1.MesoscaleState.has(this.plugin)) {
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: this.renderGraphics() });
}
const disabled = this.state.isDisabled;
const groupBy = this.groupBy;
const options = [];
roots.forEach((c, i) => {
options.push([`${i}`, c.obj.label]);
});
const groupParam = param_definition_1.ParamDefinition.Select(options[0][0], options);
const root = roots.length === 1 ? roots[0] : roots[groupBy];
const filter = this.filter;
return (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [this.renderGraphics(), (0, jsx_runtime_1.jsxs)("div", { id: 'searchtree', className: `msp-flex-row msp-control-row`, style: { margin: '5px', marginBottom: '10px' }, children: [(0, jsx_runtime_1.jsx)("input", { type: 'text', ref: this.filterRef, value: filter, placeholder: 'Search', onChange: e => this.setFilter(e.target.value), disabled: disabled, onBlur: () => this.filterFocus = false }), (0, jsx_runtime_1.jsx)(common_1.IconButton, { svg: icons_1.CloseSvg, toggleState: false, disabled: disabled, onClick: () => this.setFilter('') })] }), options.length > 1 && (0, jsx_runtime_1.jsx)("div", { id: 'grouptree', style: { margin: '5px', marginBottom: '10px' }, children: (0, jsx_runtime_1.jsx)(parameters_1.SelectControl, { name: 'Group By', param: groupParam, value: `${groupBy}`, onChange: (e) => { this.setGroupBy(parseInt(e.value)); } }) }), (0, jsx_runtime_1.jsx)("div", { id: 'tree', style: { position: 'relative', overflowY: 'auto', borderBottom: '1px solid #000', maxHeight: '600px' }, children: (0, jsx_runtime_1.jsx)(GroupNode, { filter: filter, cell: root, depth: 0 }) })] });
}
}
exports.EntityControls = EntityControls;
class Node extends base_1.PluginUIComponent {
is(e) {
return e.ref === this.ref && e.state === this.props.cell.parent;
}
get ref() {
return this.props.cell.transform.ref;
}
get cell() {
return this.props.cell;
}
get roots() {
return (0, state_1.getRoots)(this.plugin);
}
componentDidMount() {
this.subscribe(this.plugin.state.data.behaviors.isUpdating, v => {
this.setState({ isDisabled: v });
});
this.subscribe(this.plugin.state.events.cell.stateUpdated, e => {
if (!this.state.isDisabled && this.is(e)) {
this.forceUpdate();
}
});
}
}
class GroupNode extends Node {
constructor() {
super(...arguments);
this.state = {
isCollapsed: !!this.props.cell.state.isCollapsed,
action: undefined,
isDisabled: false,
};
this.toggleExpanded = (e) => {
commands_1.PluginCommands.State.ToggleExpanded(this.plugin, { state: this.cell.parent, ref: this.ref });
};
this.toggleColor = (e) => {
this.setState({ action: this.state.action === 'color' ? undefined : 'color' });
};
this.toggleClip = () => {
this.setState({ action: this.state.action === 'clip' ? undefined : 'clip' });
};
this.toggleRoot = () => {
this.setState({ action: this.state.action === 'root' ? undefined : 'root' });
};
this.showInfo = (e) => {
e.preventDefault();
const d = (0, state_1.getCellDescription)(this.cell); // '### ' + this.cell?.obj?.label + '\n\n' + this.cell?.obj?.description;
state_1.MesoscaleState.set(this.plugin, { focusInfo: `${d}` });
};
this.highlight = (e) => {
var _a, _b, _c;
e.preventDefault();
(_a = this.plugin.canvas3d) === null || _a === void 0 ? void 0 : _a.mark({ loci: loci_1.EveryLoci }, marker_action_1.MarkerAction.RemoveHighlight);
for (const r of this.allFilteredEntities) {
const repr = (_b = r.obj) === null || _b === void 0 ? void 0 : _b.data.repr;
if (repr) {
(_c = this.plugin.canvas3d) === null || _c === void 0 ? void 0 : _c.mark({ repr, loci: loci_1.EveryLoci }, marker_action_1.MarkerAction.Highlight);
}
}
};
this.clearHighlight = (e) => {
var _a;
e.preventDefault();
(_a = this.plugin.canvas3d) === null || _a === void 0 ? void 0 : _a.mark({ loci: loci_1.EveryLoci }, marker_action_1.MarkerAction.RemoveHighlight);
e.currentTarget.blur();
};
this.toggleVisible = (e) => {
commands_1.PluginCommands.State.ToggleVisibility(this.plugin, { state: this.cell.parent, ref: this.ref });
const isHidden = this.cell.state.isHidden;
for (const r of this.allFilteredEntities) {
this.plugin.state.data.updateCellState(r.transform.ref, { isHidden });
}
this.plugin.build().to(this.ref).update(old => {
old.hidden = isHidden;
}).commit();
};
this.updateColor = (values) => {
const update = this.plugin.state.data.build();
const { value, illustrative, type, lightness, alpha, emissive } = values;
const entities = this.filteredEntities;
let groupColors = [];
if (type === 'generate') {
groupColors = (0, state_1.getDistinctGroupColors)(entities.length, value, values.variability, values.shift);
}
for (let i = 0; i < entities.length; ++i) {
const c = type === 'generate' ? groupColors[i] : value;
update.to(entities[i]).update(old => {
if (old.type) {
if (illustrative) {
old.colorTheme = { name: 'illustrative', params: { style: { name: 'uniform', params: { value: c, lightness } } } };
}
else {
old.colorTheme = { name: 'uniform', params: { value: c, lightness } };
}
old.type.params.alpha = alpha;
old.type.params.xrayShaded = alpha < 1 ? 'inverted' : false;
old.type.params.emissive = emissive;
}
else {
old.coloring.params.color = c;
old.coloring.params.lightness = lightness;
old.alpha = alpha;
old.xrayShaded = alpha < 1 ? true : false;
old.emissive = emissive;
}
});
}
update.to(this.ref).update(old => {
old.color = values;
});
for (const r of this.roots) {
update.to(r).update(old => {
old.color.type = 'custom';
});
}
update.commit();
};
this.updateRoot = async (values) => {
var _a, _b;
await (0, state_1.updateColors)(this.plugin, values, (_a = this.cell.params) === null || _a === void 0 ? void 0 : _a.values.tag, this.props.filter);
const update = this.plugin.state.data.build();
for (const r of this.roots) {
if (r !== this.cell) {
update.to(r).update(old => {
old.color.type = 'custom';
});
const others = (0, state_1.getAllLeafGroups)(this.plugin, (_b = r.params) === null || _b === void 0 ? void 0 : _b.values.tag);
for (const o of others) {
update.to(o).update(old => {
old.color.type = 'custom';
});
}
}
}
update.to(this.ref).update(old => {
old.color = values;
});
update.commit();
};
this.updateClip = (values) => {
const update = this.plugin.state.data.build();
const clipObjects = (0, state_1.getClipObjects)(values, this.plugin.canvas3d.boundingSphere);
for (const r of this.allFilteredEntities) {
update.to(r).update(old => {
if (old.type) {
old.type.params.clip.objects = clipObjects;
}
else {
old.clip.objects = clipObjects;
}
});
}
for (const g of this.allGroups) {
update.to(g).update(old => {
old.clip = values;
});
}
update.commit();
};
this.updateLod = (values) => {
state_1.MesoscaleState.set(this.plugin, { graphics: 'custom' });
this.plugin.customState.graphicsMode = 'custom';
const update = this.plugin.state.data.build();
for (const r of this.allFilteredEntities) {
update.to(r).update(old => {
if (old.type) {
old.type.params.lodLevels = values.lodLevels;
old.type.params.cellSize = values.cellSize;
old.type.params.batchSize = values.batchSize;
old.type.params.approximate = values.approximate;
}
});
}
for (const g of this.allGroups) {
update.to(g).update(old => {
old.lod = values;
});
}
update.commit();
};
this.update = (props) => {
this.plugin.state.data.build().to(this.ref).update(props);
};
}
get groups() {
var _a;
return (0, state_1.getGroups)(this.plugin, (_a = this.cell.params) === null || _a === void 0 ? void 0 : _a.values.tag);
}
get allGroups() {
var _a;
const allGroups = (0, state_1.getAllGroups)(this.plugin, (_a = this.cell.params) === null || _a === void 0 ? void 0 : _a.values.tag);
allGroups.push(this.cell);
return allGroups;
}
get entities() {
var _a;
return (0, state_1.getEntities)(this.plugin, (_a = this.cell.params) === null || _a === void 0 ? void 0 : _a.values.tag);
}
get filteredEntities() {
var _a;
return (0, state_1.getFilteredEntities)(this.plugin, (_a = this.cell.params) === null || _a === void 0 ? void 0 : _a.values.tag, this.props.filter);
}
get allEntities() {
var _a;
return (0, state_1.getAllEntities)(this.plugin, (_a = this.cell.params) === null || _a === void 0 ? void 0 : _a.values.tag);
}
get allFilteredEntities() {
var _a;
return (0, state_1.getAllFilteredEntities)(this.plugin, (_a = this.cell.params) === null || _a === void 0 ? void 0 : _a.values.tag, this.props.filter);
}
renderColor() {
var _a, _b, _c;
const color = (_a = this.cell.params) === null || _a === void 0 ? void 0 : _a.values.color;
if (((_b = this.cell.params) === null || _b === void 0 ? void 0 : _b.values.color.type) === 'uniform') {
const style = {
backgroundColor: color_1.Color.toStyle(color.value),
minWidth: 32,
width: 32,
borderRight: `6px solid ${color_1.Color.toStyle(color_1.Color.lighten(color.value, color.lightness))}`
};
return (0, jsx_runtime_1.jsx)(common_1.Button, { style: style, onClick: this.toggleColor });
}
else if (((_c = this.cell.params) === null || _c === void 0 ? void 0 : _c.values.color.type) === 'generate') {
const style = {
minWidth: 32,
width: 32,
borderRight: `6px solid ${color_1.Color.toStyle(color_1.Color.lighten(color.value, color.lightness))}`
};
return (0, jsx_runtime_1.jsx)(common_1.IconButton, { style: style, svg: icons_1.BrushSvg, toggleState: false, small: true, onClick: this.toggleColor });
}
else {
return (0, jsx_runtime_1.jsx)(common_1.IconButton, { svg: icons_1.BrushSvg, toggleState: false, small: true, onClick: this.toggleColor });
}
}
render() {
var _a, _b, _c, _d, _e;
if (this.allFilteredEntities.length === 0)
return;
const state = this.cell.state;
const disabled = false;
const groupLabel = this.cell.obj.label;
const depth = this.props.depth;
const colorValue = (_a = this.cell.params) === null || _a === void 0 ? void 0 : _a.values.color;
const rootValue = (_b = this.cell.params) === null || _b === void 0 ? void 0 : _b.values.color;
const clipValue = (_c = this.cell.params) === null || _c === void 0 ? void 0 : _c.values.clip;
const lodValue = (_d = this.cell.params) === null || _d === void 0 ? void 0 : _d.values.lod;
const isRoot = (_e = this.cell.params) === null || _e === void 0 ? void 0 : _e.values.root;
const groups = this.groups;
const entities = this.entities;
const label = (0, jsx_runtime_1.jsx)(common_1.Button, { className: `msp-btn-tree-label`, noOverflow: true, disabled: disabled, onMouseEnter: this.highlight, onMouseLeave: this.clearHighlight, onClick: this.showInfo, children: (0, jsx_runtime_1.jsx)("span", { title: groupLabel, children: groupLabel }) });
const expand = (0, jsx_runtime_1.jsx)(common_1.IconButton, { svg: state.isCollapsed ? icons_1.ArrowRightSvg : icons_1.ArrowDropDownSvg, flex: '20px', disabled: disabled, onClick: this.toggleExpanded, transparent: true, className: 'msp-no-hover-outline', style: { visibility: groups.length > 0 || entities.length > 0 ? 'visible' : 'hidden' } });
const color = (entities.length > 0 && !isRoot) && this.renderColor();
const root = (isRoot && this.allGroups.length > 1) && (0, jsx_runtime_1.jsx)(common_1.IconButton, { svg: icons_1.BrushSvg, toggleState: false, disabled: disabled, small: true, onClick: this.toggleRoot });
const clip = (0, jsx_runtime_1.jsx)(common_1.IconButton, { svg: icons_1.ContentCutSvg, toggleState: false, disabled: disabled, small: true, onClick: this.toggleClip });
const visibility = (0, jsx_runtime_1.jsx)(common_1.IconButton, { svg: state.isHidden ? icons_1.VisibilityOffOutlinedSvg : icons_1.VisibilityOutlinedSvg, toggleState: false, disabled: disabled, small: true, onClick: this.toggleVisible });
const loadColorButton = (depth === 0) && (0, jsx_runtime_1.jsx)(states_1.ColorLoaderControls, { plugin: this.plugin });
return (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: `msp-flex-row`, style: { margin: `1px 5px 1px ${depth * 10 + 5}px` }, children: [expand, label, root || color, loadColorButton, clip, visibility] }), this.state.action === 'color' && (0, jsx_runtime_1.jsx)("div", { style: { marginRight: 5 }, className: 'msp-accent-offset', children: (0, jsx_runtime_1.jsx)(common_1.ControlGroup, { header: 'Color', initialExpanded: true, hideExpander: true, hideOffset: true, onHeaderClick: this.toggleColor, topRightIcon: icons_1.CloseSvg, noTopMargin: true, childrenClassName: 'msp-viewport-controls-panel-controls', children: (0, jsx_runtime_1.jsx)(parameters_1.ParameterControls, { params: state_1.ColorParams, values: colorValue, onChangeValues: this.updateColor }) }) }), this.state.action === 'clip' && (0, jsx_runtime_1.jsx)("div", { style: { marginRight: 5 }, className: 'msp-accent-offset', children: (0, jsx_runtime_1.jsxs)(common_1.ControlGroup, { header: 'Clip', initialExpanded: true, hideExpander: true, hideOffset: true, onHeaderClick: this.toggleClip, topRightIcon: icons_1.CloseSvg, noTopMargin: true, childrenClassName: 'msp-viewport-controls-panel-controls', children: [(0, jsx_runtime_1.jsx)(parameters_1.ParameterControls, { params: state_1.SimpleClipParams, values: clipValue, onChangeValues: this.updateClip }), (0, jsx_runtime_1.jsx)(parameters_1.ParameterControls, { params: state_1.LodParams, values: lodValue, onChangeValues: this.updateLod })] }) }), this.state.action === 'root' && (0, jsx_runtime_1.jsx)("div", { style: { marginRight: 5 }, className: 'msp-accent-offset', children: (0, jsx_runtime_1.jsx)(common_1.ControlGroup, { header: 'Color', initialExpanded: true, hideExpander: true, hideOffset: true, onHeaderClick: this.toggleRoot, topRightIcon: icons_1.CloseSvg, noTopMargin: true, childrenClassName: 'msp-viewport-controls-panel-controls', children: (0, jsx_runtime_1.jsx)(parameters_1.ParameterControls, { params: state_1.RootParams, values: rootValue, onChangeValues: this.updateRoot }) }) }), (!state.isCollapsed) && (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [groups.map(c => {
return (0, jsx_runtime_1.jsx)(GroupNode, { filter: this.props.filter, cell: c, depth: depth + 1 }, c.transform.ref);
}), this.filteredEntities.map(c => {
return (0, jsx_runtime_1.jsx)(EntityNode, { cell: c, depth: depth + 1 }, c.transform.ref);
})] })] });
}
}
exports.GroupNode = GroupNode;
class EntityNode extends Node {
constructor() {
super(...arguments);
this.state = {
action: undefined,
isDisabled: false,
};
this.clipMapping = (0, state_1.createClipMapping)(this);
this.toggleVisible = (e) => {
e.preventDefault();
commands_1.PluginCommands.State.ToggleVisibility(this.plugin, { state: this.props.cell.parent, ref: this.ref });
e.currentTarget.blur();
};
this.toggleColor = (e) => {
var _a;
if (e === null || e === void 0 ? void 0 : e.ctrlKey) {
this.updateLightness({ lightness: ((_a = this.lightnessValue) === null || _a === void 0 ? void 0 : _a.lightness) ? 0 : state_1.DimLightness });
e.preventDefault();
}
else {
this.setState({ action: this.state.action === 'color' ? undefined : 'color' });
}
};
this.toggleClip = () => {
this.setState({ action: this.state.action === 'clip' ? undefined : 'clip' });
};
this.highlight = (e) => {
var _a, _b, _c, _d;
e.preventDefault();
(_a = this.plugin.canvas3d) === null || _a === void 0 ? void 0 : _a.mark({ loci: loci_1.EveryLoci }, marker_action_1.MarkerAction.RemoveHighlight);
const repr = (_c = (_b = this.cell) === null || _b === void 0 ? void 0 : _b.obj) === null || _c === void 0 ? void 0 : _c.data.repr;
if (repr) {
(_d = this.plugin.canvas3d) === null || _d === void 0 ? void 0 : _d.mark({ repr, loci: loci_1.EveryLoci }, marker_action_1.MarkerAction.Highlight);
}
e.currentTarget.blur();
};
this.clearHighlight = (e) => {
var _a;
e.preventDefault();
(_a = this.plugin.canvas3d) === null || _a === void 0 ? void 0 : _a.mark({ loci: loci_1.EveryLoci }, marker_action_1.MarkerAction.RemoveHighlight);
e.currentTarget.blur();
};
this.toggleSelect = (e) => {
var _a;
e.preventDefault();
const cell = this.cell;
if (!(((_a = cell === null || cell === void 0 ? void 0 : cell.obj) === null || _a === void 0 ? void 0 : _a.data.sourceData) instanceof structure_1.Structure))
return;
const loci = structure_1.Structure.toStructureElementLoci(cell.obj.data.sourceData);
this.plugin.managers.interactivity.lociSelects.toggle({ loci }, false);
};
this.center = (e) => {
var _a;
e.preventDefault();
const cell = this.cell;
if (!(((_a = cell === null || cell === void 0 ? void 0 : cell.obj) === null || _a === void 0 ? void 0 : _a.data.sourceData) instanceof structure_1.Structure))
return;
const loci = structure_1.Structure.toStructureElementLoci(cell.obj.data.sourceData);
centerLoci(this.plugin, loci);
};
this.handleClick = (e) => {
var _a, _b, _c, _d, _e, _f, _g;
if (e.ctrlKey) {
this.toggleSelect(e);
}
else {
const d = (0, state_1.getEntityDescription)(this.plugin, this.cell);
if (((_b = (_a = this.cell) === null || _a === void 0 ? void 0 : _a.obj) === null || _b === void 0 ? void 0 : _b.data.sourceData.state.models.length) !== 0) {
const repr = (_d = (_c = this.cell) === null || _c === void 0 ? void 0 : _c.obj) === null || _d === void 0 ? void 0 : _d.data.repr;
if (repr) {
// for fiber need to think how to handle.
const aloci = repr.getAllLoci()[0];
const locis = loci_1.Loci.normalize(aloci, 'chainInstances');
const nChain = aloci.structure.state.unitSymmetryGroups.length;
let index = state_1.MesoscaleState.get(this.plugin).index + 1;
if (index * nChain >= locis.elements.length)
index = 0;
const elems = locis.elements.slice(index * nChain, ((index + 1) * nChain)); // end index is not included
const loci = element_1.StructureElement.Loci(aloci.structure, elems);
const sphere = loci_1.Loci.getBoundingSphere(loci) || (0, geometry_1.Sphere3D)();
const state = this.plugin.state.behaviors;
const selections = state.select(mol_state_1.StateSelection.Generators.ofTransformer(camera_1.MesoFocusLoci));
const params = selections.length === 1 ? (_e = selections[0].obj) === null || _e === void 0 ? void 0 : _e.data.params : undefined;
if (!params.centerOnly) {
this.plugin.managers.camera.focusSphere(sphere, params);
}
else {
const snapshot = (_f = this.plugin.canvas3d) === null || _f === void 0 ? void 0 : _f.camera.getCenter(sphere.center);
(_g = this.plugin.canvas3d) === null || _g === void 0 ? void 0 : _g.requestCameraReset({ durationMs: params.durationMs, snapshot });
}
state_1.MesoscaleState.set(this.plugin, { index: index, focusInfo: `${d}` });
}
}
else {
this.center(e);
state_1.MesoscaleState.set(this.plugin, { focusInfo: `${d}` });
}
}
};
this.updateColor = ({ value }) => {
const update = this.plugin.state.data.build();
for (const g of this.groups) {
update.to(g.transform.ref).update(old => {
old.color.type = 'custom';
});
}
for (const r of this.roots) {
update.to(r).update(old => {
old.color.type = 'custom';
});
}
update.to(this.ref).update(old => {
if (old.colorTheme) {
if (old.colorTheme.name === 'illustrative') {
old.colorTheme.params.style.params.value = value;
}
else {
old.colorTheme.params.value = value;
}
}
else if (old.coloring) {
old.coloring.params.color = value;
}
});
update.commit();
};
this.updateIllustrative = (values) => {
return this.plugin.build().to(this.ref).update(old => {
if (old.colorTheme) {
if (old.colorTheme.name !== 'illustrative' && values.illustrative) {
old.colorTheme = { name: 'illustrative', params: { style: { name: 'uniform', params: { value: old.colorTheme.params.value, lightness: old.colorTheme.params.lightness } } } };
}
else if (old.colorTheme.name === 'illustrative' && !values.illustrative) {
old.colorTheme = { name: 'uniform', params: { value: old.colorTheme.params.style.params.value, lightness: old.colorTheme.params.style.params.lightness } };
}
}
}).commit();
};
this.updateLightness = (values) => {
return this.plugin.build().to(this.ref).update(old => {
if (old.colorTheme) {
if (old.colorTheme.name === 'illustrative') {