|
@@ -9,16 +9,20 @@ import { PluginStateObject } from '../objects';
|
|
|
import { StateTransforms } from '../transforms';
|
|
|
import { StateSelection } from 'mol-state/state/selection';
|
|
|
import { PluginCommands } from 'mol-plugin/command';
|
|
|
-import { ParamDefinition } from 'mol-util/param-definition';
|
|
|
+import { ParamDefinition as PD } from 'mol-util/param-definition';
|
|
|
|
|
|
export const AnimateModelIndex = PluginStateAnimation.create({
|
|
|
name: 'built-in.animate-model-index',
|
|
|
display: { name: 'Animate Model Index' },
|
|
|
params: () => ({
|
|
|
- direction: ParamDefinition.Select('forward', [['forward', 'Forward'], ['backward', 'Backward']]),
|
|
|
- maxFPS: ParamDefinition.Numeric(3, { min: 0.5, max: 30, step: 0.5 })
|
|
|
+ mode: PD.MappedStatic('once', {
|
|
|
+ once: PD.Group({ direction: PD.Select('forward', [['forward', 'Forward'], ['backward', 'Backward']]) }),
|
|
|
+ palindrome: PD.Group({ }),
|
|
|
+ loop: PD.Group({ }),
|
|
|
+ }, { options: [['once', 'Once'], ['palindrome', 'Palindrome'], ['loop', 'Loop']] }),
|
|
|
+ maxFPS: PD.Numeric(3, { min: 0.5, max: 30, step: 0.5 })
|
|
|
}),
|
|
|
- initialState: () => ({ }),
|
|
|
+ initialState: () => ({} as { palindromeDirections?: { [id: string]: -1 | 1 | undefined } }),
|
|
|
async apply(animState, t, ctx) {
|
|
|
// limit fps
|
|
|
if (t.current > 0 && t.current - t.lastApplied < 1000 / ctx.params.maxFPS) {
|
|
@@ -31,7 +35,9 @@ export const AnimateModelIndex = PluginStateAnimation.create({
|
|
|
|
|
|
const update = state.build();
|
|
|
|
|
|
- const dir = ctx.params.direction === 'backward' ? -1 : 1;
|
|
|
+ const params = ctx.params;
|
|
|
+ const palindromeDirections = animState.palindromeDirections || { };
|
|
|
+ let isEnd = false;
|
|
|
|
|
|
for (const m of models) {
|
|
|
const parent = StateSelection.findAncestorOfType(state.tree, state.cells, m.transform.ref, [PluginStateObject.Molecule.Trajectory]);
|
|
@@ -39,13 +45,35 @@ export const AnimateModelIndex = PluginStateAnimation.create({
|
|
|
const traj = parent.obj as PluginStateObject.Molecule.Trajectory;
|
|
|
update.to(m.transform.ref).update(StateTransforms.Model.ModelFromTrajectory,
|
|
|
old => {
|
|
|
- let modelIndex = (old.modelIndex + dir) % traj.data.length;
|
|
|
- if (modelIndex < 0) modelIndex += traj.data.length;
|
|
|
+ const len = traj.data.length;
|
|
|
+ let dir: -1 | 1 = 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 };
|
|
|
});
|
|
|
}
|
|
|
|
|
|
await PluginCommands.State.Update.dispatch(ctx.plugin, { state, tree: update });
|
|
|
+
|
|
|
+ if (params.mode.name === 'once' && isEnd) return { kind: 'finished' };
|
|
|
+ if (params.mode.name === 'palindrome') return { kind: 'next', state: { palindromeDirections } };
|
|
|
return { kind: 'next', state: {} };
|
|
|
}
|
|
|
})
|