molstar
Version:
A comprehensive macromolecular library.
143 lines (142 loc) • 7.26 kB
JavaScript
/**
* Copyright (c) 2023-2024 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 { Canvas3DParams } from '../../mol-canvas3d/canvas3d';
import { Vec3 } from '../../mol-math/linear-algebra';
import { getFocusSnapshot, getPluginBoundingSphere } from '../../mol-plugin-state/manager/focus-camera/focus-object';
import { PluginCommands } from '../../mol-plugin/commands';
import { ColorNames } from '../../mol-util/color/names';
import { decodeColor } from './helpers/utils';
import { MVSTreeSchema } from './tree/mvs/mvs-tree';
const DefaultFocusOptions = {
minRadius: 5,
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) {
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 };
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 });
}
/** Return the distance adjustment ratio for conversion from the "reference camera"
* to a camera with an arbitrary field of view `fov`. */
function distanceAdjustment(mode, fov) {
if (mode === 'orthographic')
return 1 / (2 * Math.tan(fov / 2));
else
return 1 / (2 * Math.sin(fov / 2));
}
/** Return the position for a camera with an arbitrary field of view `fov`
* necessary to just fit into view the same sphere (with center at `target`)
* as the "reference camera" placed at `refPosition` would fit, while keeping the camera orientation.
* The "reference camera" is a camera which can just fit into view a sphere of radius R with center at distance 2R
* (this corresponds to FOV = 2 * asin(1/2) in perspective mode or FOV = 2 * atan(1/2) in orthographic mode). */
function fovAdjustedPosition(target, refPosition, mode, fov) {
const delta = Vec3.sub(Vec3(), refPosition, target);
const adjustment = distanceAdjustment(mode, fov);
return Vec3.scaleAndAdd(delta, target, delta, adjustment); // return target + delta * adjustment
}
/** 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;
}
/** Set canvas properties based on a canvas node params. */
export function setCanvas(plugin, params) {
var _a;
(_a = plugin.canvas3d) === null || _a === void 0 ? void 0 : _a.setProps(old => modifyCanvasProps(old, params));
}
/** Create a deep copy of `oldCanvasProps` with values modified according to a canvas node params. */
export function modifyCanvasProps(oldCanvasProps, params) {
var _a;
const backgroundColor = (_a = decodeColor(params === null || params === void 0 ? void 0 : params.background_color)) !== null && _a !== void 0 ? _a : DefaultCanvasBackgroundColor;
return {
...oldCanvasProps,
renderer: {
...oldCanvasProps.renderer,
backgroundColor: backgroundColor,
},
};
}