UNPKG

molstar

Version:

A comprehensive macromolecular library.

157 lines (156 loc) 8.24 kB
"use strict"; /** * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> */ Object.defineProperty(exports, "__esModule", { value: true }); exports.AnimateModelIndex = void 0; const commands_1 = require("../../../mol-plugin/commands"); const mol_state_1 = require("../../../mol-state"); const param_definition_1 = require("../../../mol-util/param-definition"); const objects_1 = require("../../objects"); const transforms_1 = require("../../transforms"); const model_1 = require("../model"); exports.AnimateModelIndex = model_1.PluginStateAnimation.create({ name: 'built-in.animate-model-index', display: { name: 'Animate Trajectory' }, isExportable: true, params: () => ({ mode: param_definition_1.ParamDefinition.MappedStatic('loop', { palindrome: param_definition_1.ParamDefinition.Group({}), loop: param_definition_1.ParamDefinition.Group({ direction: param_definition_1.ParamDefinition.Select('forward', [['forward', 'Forward'], ['backward', 'Backward']]) }), once: param_definition_1.ParamDefinition.Group({ direction: param_definition_1.ParamDefinition.Select('forward', [['forward', 'Forward'], ['backward', 'Backward']]) }, { isFlat: true }) }, { options: [['palindrome', 'Palindrome'], ['loop', 'Loop'], ['once', 'Once']] }), duration: param_definition_1.ParamDefinition.MappedStatic('fixed', { fixed: param_definition_1.ParamDefinition.Group({ durationInS: param_definition_1.ParamDefinition.Numeric(5, { min: 1, max: 120, step: 0.1 }, { description: 'Duration in seconds' }) }, { isFlat: true }), computed: param_definition_1.ParamDefinition.Group({ targetFps: param_definition_1.ParamDefinition.Numeric(30, { min: 5, max: 250, step: 1 }, { label: 'Target FPS' }) }, { isFlat: true }), sequential: param_definition_1.ParamDefinition.Group({ maxFps: param_definition_1.ParamDefinition.Numeric(30, { min: 5, max: 60, step: 1 }) }, { isFlat: true }) }) }), canApply(ctx) { const state = ctx.state.data; const models = state.select(mol_state_1.StateSelection.Generators.ofTransformer(transforms_1.StateTransforms.Model.ModelFromTrajectory)); for (const m of models) { const parent = mol_state_1.StateSelection.findAncestorOfType(state.tree, state.cells, m.transform.ref, objects_1.PluginStateObject.Molecule.Trajectory); if (parent && parent.obj && parent.obj.data.frameCount > 1) return { canApply: true }; } return { canApply: false, reason: 'No trajectory to animate' }; }, getDuration: (p, ctx) => { var _a; if (((_a = p.duration) === null || _a === void 0 ? void 0 : _a.name) === 'fixed') { return { kind: 'fixed', durationMs: p.duration.params.durationInS * 1000 }; } else if (p.duration.name === 'computed') { const state = ctx.state.data; const models = state.select(mol_state_1.StateSelection.Generators.ofTransformer(transforms_1.StateTransforms.Model.ModelFromTrajectory)); let maxDuration = 0; for (const m of models) { const parent = mol_state_1.StateSelection.findAncestorOfType(state.tree, state.cells, m.transform.ref, objects_1.PluginStateObject.Molecule.Trajectory); if (!parent || !parent.obj) continue; const traj = parent.obj; maxDuration = Math.max(Math.ceil(1000 * traj.data.frameCount / p.duration.params.targetFps), maxDuration); } return { kind: 'fixed', durationMs: maxDuration }; } return { kind: 'unknown' }; }, initialState: () => ({}), async apply(animState, t, ctx) { // limit fps if (ctx.params.duration.name === 'sequential' && t.current > 0 && t.current - t.lastApplied < 1000 / ctx.params.duration.params.maxFps) { return { kind: 'skip' }; } const state = ctx.plugin.state.data; const models = state.select(mol_state_1.StateSelection.Generators.ofTransformer(transforms_1.StateTransforms.Model.ModelFromTrajectory)); if (models.length === 0) { // nothing more to do here return { kind: 'finished' }; } const update = state.build(); const params = ctx.params; const palindromeDirections = animState.palindromeDirections || {}; let isEnd = false, allSingles = true; for (const m of models) { const parent = mol_state_1.StateSelection.findAncestorOfType(state.tree, state.cells, m.transform.ref, objects_1.PluginStateObject.Molecule.Trajectory); if (!parent || !parent.obj) continue; const traj = parent.obj; if (traj.data.frameCount <= 1) continue; update.to(m).update(old => { const len = traj.data.frameCount; if (len !== 1) { allSingles = false; } else { return old; } if (params.duration.name === 'sequential') { let dir = 1; if (params.mode.name === 'once') { dir = params.mode.params.direction === 'backward' ? -1 : 1; // if we are at start or end already, do nothing. if ((dir === -1 && old.modelIndex === 0) || (dir === 1 && old.modelIndex === len - 1)) { isEnd = true; return old; } } else if (params.mode.name === 'palindrome') { if (old.modelIndex === 0) dir = 1; else if (old.modelIndex === len - 1) dir = -1; else dir = palindromeDirections[m.transform.ref] || 1; } palindromeDirections[m.transform.ref] = dir; let modelIndex = (old.modelIndex + dir) % len; if (modelIndex < 0) modelIndex += len; isEnd = isEnd || (dir === -1 && modelIndex === 0) || (dir === 1 && modelIndex === len - 1); return { modelIndex }; } else { const durationInMs = params.duration.name === 'fixed' ? params.duration.params.durationInS * 1000 : Math.ceil(1000 * traj.data.frameCount / params.duration.params.targetFps); if (params.mode.name === 'once' && t.current >= durationInMs) { isEnd = true; return { modelIndex: traj.data.frameCount - 1 }; } let phase = (t.current % durationInMs) / durationInMs; if (params.mode.name === 'loop') { if (params.mode.params.direction === 'backward') { phase = 1 - phase; } } if (params.mode.name === 'palindrome') { phase = 2 * phase; if (phase > 1) phase = 2 - phase; } const modelIndex = Math.min(Math.floor(traj.data.frameCount * phase), traj.data.frameCount - 1); return { modelIndex }; } }); } if (!allSingles) { await commands_1.PluginCommands.State.Update(ctx.plugin, { state, tree: update, options: { doNotLogTiming: true } }); } if (allSingles || (params.mode.name === 'once' && isEnd)) return { kind: 'finished' }; if (params.mode.name === 'palindrome') return { kind: 'next', state: { palindromeDirections } }; return { kind: 'next', state: {} }; } });