molstar
Version:
A comprehensive macromolecular library.
214 lines (213 loc) • 12.3 kB
JavaScript
/**
* Copyright (c) 2023-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Adam Midlik <midlik@gmail.com>
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { CameraFogParams, Canvas3DParams, DefaultCanvas3DParams } from '../../mol-canvas3d/canvas3d.js';
import { TrackballControlsParams } from '../../mol-canvas3d/controls/trackball.js';
import { BackgroundParams } from '../../mol-canvas3d/passes/background.js';
import { BloomParams } from '../../mol-canvas3d/passes/bloom.js';
import { DofParams } from '../../mol-canvas3d/passes/dof.js';
import { OutlineParams } from '../../mol-canvas3d/passes/outline.js';
import { ShadowParams } from '../../mol-canvas3d/passes/shadow.js';
import { SsaoParams } from '../../mol-canvas3d/passes/ssao.js';
import { Vec3 } from '../../mol-math/linear-algebra.js';
import { getFocusSnapshot, getPluginBoundingSphere } from '../../mol-plugin-state/manager/focus-camera/focus-object.js';
import { PluginCommands } from '../../mol-plugin/commands.js';
import { fovAdjustedPosition } from '../../mol-util/camera.js';
import { ColorNames } from '../../mol-util/color/names.js';
import { deepClone } from '../../mol-util/object.js';
import { ParamDefinition } from '../../mol-util/param-definition.js';
import { decodeColor } from './helpers/utils.js';
import { MVSTreeSchema } from './tree/mvs/mvs-tree.js';
const DefaultFocusOptions = {
minRadius: 1,
extraRadius: 0,
};
const DefaultCanvasBackgroundColor = ColorNames.white;
const _tmpVec = Vec3();
/** Set the camera position to the current position (thus suppress automatic adjustment). */
export async function suppressCameraAutoreset(plugin) {
var _a;
const snapshot = { ...(_a = plugin.canvas3d) === null || _a === void 0 ? void 0 : _a.camera.state, radius: Infinity }; // `radius: Infinity` avoids clipping when the scene expands
adjustSceneRadiusFactor(plugin, snapshot.target);
await PluginCommands.Camera.SetSnapshot(plugin, { snapshot });
}
/** Set the camera based on a camera node params. */
export async function setCamera(plugin, params) {
const snapshot = cameraParamsToCameraSnapshot(plugin, params);
adjustSceneRadiusFactor(plugin, snapshot.target);
await PluginCommands.Camera.SetSnapshot(plugin, { snapshot });
}
export function cameraParamsToCameraSnapshot(plugin, params) {
var _a;
const target = Vec3.create(...params.target);
let position = Vec3.create(...params.position);
const radius = Vec3.distance(target, position) / 2;
if (plugin.canvas3d)
position = fovAdjustedPosition(target, position, plugin.canvas3d.camera.state.mode, plugin.canvas3d.camera.state.fov);
const up = Vec3.create(...params.up);
Vec3.orthogonalize(up, Vec3.sub(_tmpVec, target, position), up);
const snapshot = {
target,
position,
up,
radius,
radiusMax: radius,
minNear: (_a = params.near) !== null && _a !== void 0 ? _a : undefined,
};
return snapshot;
}
/** Focus the camera on the bounding sphere of a (sub)structure (or on the whole scene if `structureNodeSelector` is undefined).
* Orient the camera based on a focus node params. **/
export async function setFocus(plugin, focuses) {
const snapshot = getFocusSnapshot(plugin, {
...snapshotFocusInfoFromMvsFocuses(focuses),
minRadius: DefaultFocusOptions.minRadius,
});
if (!snapshot)
return;
resetSceneRadiusFactor(plugin);
await PluginCommands.Camera.SetSnapshot(plugin, { snapshot });
}
function snapshotFocusInfoFromMvsFocuses(focuses) {
var _a, _b;
const lastFocus = (focuses.length > 0) ? focuses[focuses.length - 1] : undefined;
const direction = (_a = lastFocus === null || lastFocus === void 0 ? void 0 : lastFocus.params.direction) !== null && _a !== void 0 ? _a : MVSTreeSchema.nodes.focus.params.fields.direction.default;
const up = (_b = lastFocus === null || lastFocus === void 0 ? void 0 : lastFocus.params.up) !== null && _b !== void 0 ? _b : MVSTreeSchema.nodes.focus.params.fields.up.default;
return {
targets: focuses.map(f => {
var _a;
return ({
targetRef: f.target.ref === '-=root=-' ? undefined : f.target.ref, // need to treat root separately so it does not include invisible structure parts etc.
radius: (_a = f.params.radius) !== null && _a !== void 0 ? _a : undefined,
radiusFactor: f.params.radius_factor,
extraRadius: f.params.radius_extent,
});
}),
direction: Vec3.create(...direction),
up: Vec3.create(...up),
};
}
/** Adjust `sceneRadiusFactor` property so that the current scene is not cropped */
function adjustSceneRadiusFactor(plugin, cameraTarget) {
var _a;
if (!cameraTarget)
return;
const boundingSphere = getPluginBoundingSphere(plugin);
const offset = Vec3.distance(cameraTarget, boundingSphere.center);
const sceneRadiusFactor = boundingSphere.radius > 0 ? ((boundingSphere.radius + offset) / boundingSphere.radius) : 1;
(_a = plugin.canvas3d) === null || _a === void 0 ? void 0 : _a.setProps({ sceneRadiusFactor });
}
/** Reset `sceneRadiusFactor` property to the default value */
function resetSceneRadiusFactor(plugin) {
var _a;
const sceneRadiusFactor = Canvas3DParams.sceneRadiusFactor.defaultValue;
(_a = plugin.canvas3d) === null || _a === void 0 ? void 0 : _a.setProps({ sceneRadiusFactor });
}
/** Create object for PluginState.Snapshot.camera based on tree loading context and MVS snapshot metadata */
export function createPluginStateSnapshotCamera(plugin, context, metadata) {
var _a;
const camera = {
transitionStyle: 'animate',
transitionDurationInMs: (_a = metadata.previousTransitionDurationMs) !== null && _a !== void 0 ? _a : 0,
};
if (context.camera.cameraParams !== undefined) {
const currentCameraSnapshot = plugin.canvas3d.camera.getSnapshot();
const cameraSnapshot = cameraParamsToCameraSnapshot(plugin, context.camera.cameraParams);
camera.current = { ...currentCameraSnapshot, ...cameraSnapshot };
}
else {
camera.focus = snapshotFocusInfoFromMvsFocuses(context.camera.focuses);
}
return camera;
}
function optionalParams(enable, values, params, fallback) {
if (typeof enable === 'boolean') {
return enable
? { name: 'on', params: { ...ParamDefinition.getDefaultValues(params), ...values } }
: { name: 'off', params: {} };
}
return fallback;
}
function normalizeBackground(variant, prev) {
if (!variant)
return prev;
return ParamDefinition.normalizeParams(BackgroundParams, { variant }, 'children');
}
/** Create a deep copy of `oldCanvasProps` with values modified according to a canvas node params. */
export function modifyCanvasProps(oldCanvasProps, canvasNode, animationNode) {
var _a, _b, _c, _d, _e;
const params = canvasNode === null || canvasNode === void 0 ? void 0 : canvasNode.params;
const backgroundColor = (_a = decodeColor(params === null || params === void 0 ? void 0 : params.background_color)) !== null && _a !== void 0 ? _a : DefaultCanvasBackgroundColor;
const molstar_postprocessing = (_b = canvasNode === null || canvasNode === void 0 ? void 0 : canvasNode.custom) === null || _b === void 0 ? void 0 : _b.molstar_postprocessing;
const outline = molstar_postprocessing === null || molstar_postprocessing === void 0 ? void 0 : molstar_postprocessing.enable_outline;
const outlineParams = molstar_postprocessing === null || molstar_postprocessing === void 0 ? void 0 : molstar_postprocessing.outline_params;
const shadow = molstar_postprocessing === null || molstar_postprocessing === void 0 ? void 0 : molstar_postprocessing.enable_shadow;
const shadowParams = molstar_postprocessing === null || molstar_postprocessing === void 0 ? void 0 : molstar_postprocessing.shadow_params;
const occlusion = molstar_postprocessing === null || molstar_postprocessing === void 0 ? void 0 : molstar_postprocessing.enable_ssao;
const occlusionParams = molstar_postprocessing === null || molstar_postprocessing === void 0 ? void 0 : molstar_postprocessing.ssao_params;
const fog = molstar_postprocessing === null || molstar_postprocessing === void 0 ? void 0 : molstar_postprocessing.enable_fog;
const fogParams = molstar_postprocessing === null || molstar_postprocessing === void 0 ? void 0 : molstar_postprocessing.fog_params;
const dof = molstar_postprocessing === null || molstar_postprocessing === void 0 ? void 0 : molstar_postprocessing.enable_depth_of_field;
const dofParams = molstar_postprocessing === null || molstar_postprocessing === void 0 ? void 0 : molstar_postprocessing.depth_of_field_params;
const bloom = molstar_postprocessing === null || molstar_postprocessing === void 0 ? void 0 : molstar_postprocessing.enable_bloom;
const bloomParams = molstar_postprocessing === null || molstar_postprocessing === void 0 ? void 0 : molstar_postprocessing.bloom_params;
const background = molstar_postprocessing === null || molstar_postprocessing === void 0 ? void 0 : molstar_postprocessing.background;
const trackballAnimation = (_c = animationNode === null || animationNode === void 0 ? void 0 : animationNode.custom) === null || _c === void 0 ? void 0 : _c.molstar_trackball;
const trackballAnimationName = trackballAnimation === null || trackballAnimation === void 0 ? void 0 : trackballAnimation.name;
const trackballAnimationParams = (_d = trackballAnimation === null || trackballAnimation === void 0 ? void 0 : trackballAnimation.params) !== null && _d !== void 0 ? _d : {};
return {
...oldCanvasProps,
postprocessing: {
...oldCanvasProps.postprocessing,
outline: optionalParams(outline, outlineParams, OutlineParams, oldCanvasProps.postprocessing.outline),
shadow: optionalParams(shadow, shadowParams, ShadowParams, oldCanvasProps.postprocessing.shadow),
occlusion: optionalParams(occlusion, occlusionParams, SsaoParams, oldCanvasProps.postprocessing.occlusion),
dof: optionalParams(dof, dofParams, DofParams, oldCanvasProps.postprocessing.dof),
bloom: optionalParams(bloom, bloomParams, BloomParams, oldCanvasProps.postprocessing.bloom),
background: normalizeBackground(background, oldCanvasProps.postprocessing.background),
},
cameraFog: optionalParams(fog, fogParams, CameraFogParams, oldCanvasProps.cameraFog),
renderer: {
...oldCanvasProps.renderer,
backgroundColor: backgroundColor,
},
trackball: {
...oldCanvasProps === null || oldCanvasProps === void 0 ? void 0 : oldCanvasProps.trackball,
...(trackballAnimationName
? {
animate: {
name: trackballAnimationName,
params: {
...(_e = TrackballControlsParams.animate.map(trackballAnimationName)) === null || _e === void 0 ? void 0 : _e.defaultValue,
...trackballAnimationParams
}
}
}
: {}),
}
};
}
export function resetCanvasProps(plugin) {
var _a, _b;
const old = (_a = plugin.canvas3d) === null || _a === void 0 ? void 0 : _a.props;
(_b = plugin.canvas3d) === null || _b === void 0 ? void 0 : _b.setProps({
...old,
postprocessing: {
...old,
outline: deepClone(DefaultCanvas3DParams.postprocessing.outline),
shadow: deepClone(DefaultCanvas3DParams.postprocessing.shadow),
occlusion: deepClone(DefaultCanvas3DParams.postprocessing.occlusion),
dof: deepClone(DefaultCanvas3DParams.postprocessing.dof),
bloom: deepClone(DefaultCanvas3DParams.postprocessing.bloom),
background: deepClone(DefaultCanvas3DParams.postprocessing.background),
},
cameraFog: deepClone(DefaultCanvas3DParams.cameraFog),
trackball: {
...old === null || old === void 0 ? void 0 : old.trackball,
animate: { name: 'off', params: {} },
}
});
}