UNPKG

molstar

Version:

A comprehensive macromolecular library.

143 lines (142 loc) 7.26 kB
/** * 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, }, }; }