Skip to content
Snippets Groups Projects
Commit a0583118 authored by Alexander Rose's avatar Alexander Rose
Browse files

added explode-units anim and removed old anim behavior

parent 9b83ab51
No related branches found
No related tags found
No related merge requests found
......@@ -14,7 +14,6 @@ import * as StaticMisc from './behavior/static/misc'
import * as DynamicRepresentation from './behavior/dynamic/representation'
import * as DynamicCamera from './behavior/dynamic/camera'
import * as DynamicCustomProps from './behavior/dynamic/custom-props'
import * as DynamicAnimation from './behavior/dynamic/animation'
import * as DynamicLabels from './behavior/dynamic/labels'
export const BuiltInPluginBehaviors = {
......@@ -28,6 +27,5 @@ export const PluginBehaviors = {
Representation: DynamicRepresentation,
Camera: DynamicCamera,
CustomProps: DynamicCustomProps,
Animation: DynamicAnimation,
Labels: DynamicLabels
}
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { PluginContext } from 'mol-plugin/context';
import { PluginBehavior } from '../behavior';
import { ParamDefinition as PD } from 'mol-util/param-definition'
import { degToRad } from 'mol-math/misc';
import { Mat4, Vec3 } from 'mol-math/linear-algebra';
import { PluginStateObject as SO, PluginStateObject } from '../../state/objects';
import { StateObjectCell, State, StateSelection } from 'mol-state';
import { StructureUnitTransforms } from 'mol-model/structure/structure/util/unit-transforms';
import { UUID } from 'mol-util';
const StructureAnimationParams = {
rotate: PD.Boolean(false),
rotateValue: PD.Numeric(0, { min: 0, max: 360, step: 0.1 }),
explode: PD.Boolean(false),
explodeValue: PD.Numeric(0, { min: 0, max: 100, step: 0.1 }),
}
type StructureAnimationProps = PD.Values<typeof StructureAnimationParams>
/**
* TODO
* - animation class is just for testing purposes, needs better API
*/
export const StructureAnimation = PluginBehavior.create<StructureAnimationProps>({
name: 'structure-animation',
category: 'representation',
display: { name: 'Structure Animation' },
canAutoUpdate: () => true,
ctor: class extends PluginBehavior.Handler<StructureAnimationProps> {
private tmpMat = Mat4.identity()
private rotMat = Mat4.identity()
private transMat = Mat4.identity()
private animMat = Mat4.identity()
private transVec = Vec3.zero()
private rotVec = Vec3.create(0, 1, 0)
private centerVec = Vec3.zero()
private rotateAnimHandle = -1
private explodeAnimHandle = -1
private updatedUnitTransforms = new Set<SO.Molecule.Structure>()
private structureUnitTransforms = new Map<UUID, StructureUnitTransforms>()
constructor(protected ctx: PluginContext, protected params: StructureAnimationProps) {
super(ctx, params)
this.update(params)
}
private getUnitTransforms(structure: SO.Molecule.Structure) {
let unitTransforms = this.structureUnitTransforms.get(structure.id)
if (!unitTransforms) {
unitTransforms = new StructureUnitTransforms(structure.data)
this.structureUnitTransforms.set(structure.id, unitTransforms)
}
return unitTransforms
}
rotate(rad: number) {
this.updatedUnitTransforms.clear()
const state = this.ctx.state.dataState
const reprs = state.selectQ(q => q.rootsOfType(PluginStateObject.Molecule.Structure.Representation3D))
Mat4.rotate(this.rotMat, this.tmpMat, rad, this.rotVec)
for (const r of reprs) {
if (!SO.isRepresentation3D(r.obj)) return
const structure = getRootStructure(r, state)
if (!structure || !SO.Molecule.Structure.is(structure.obj)) continue
const unitTransforms = this.getUnitTransforms(structure.obj)
if (!this.updatedUnitTransforms.has(structure.obj)) {
for (let i = 0, il = structure.obj.data.units.length; i < il; ++i) {
const u = structure.obj.data.units[i]
Vec3.transformMat4(this.centerVec, u.lookup3d.boundary.sphere.center, u.conformation.operator.matrix)
Vec3.negate(this.transVec, Vec3.copy(this.transVec, this.centerVec))
Mat4.fromTranslation(this.transMat, this.transVec)
Mat4.mul(this.animMat, this.rotMat, this.transMat)
Vec3.copy(this.transVec, this.centerVec)
Mat4.fromTranslation(this.transMat, this.transVec)
Mat4.mul(this.animMat, this.transMat, this.animMat)
unitTransforms.setTransform(this.animMat, u)
}
this.updatedUnitTransforms.add(structure.obj)
}
r.obj.data.repr.setState({ unitTransforms })
this.ctx.canvas3d.add(r.obj.data.repr)
}
this.ctx.canvas3d.requestDraw(true)
}
animateRotate(play: boolean) {
if (play) {
const animateRotate = (t: number) => {
this.rotate(degToRad((t / 10) % 360))
this.rotateAnimHandle = requestAnimationFrame(animateRotate)
}
this.rotateAnimHandle = requestAnimationFrame(animateRotate)
} else {
cancelAnimationFrame(this.rotateAnimHandle)
}
}
explode(p: number) {
this.updatedUnitTransforms.clear()
const state = this.ctx.state.dataState
const reprs = state.selectQ(q => q.rootsOfType(PluginStateObject.Molecule.Structure.Representation3D));
for (const r of reprs) {
if (!SO.isRepresentation3D(r.obj)) return
const structure = getRootStructure(r, state)
if (!structure || !SO.Molecule.Structure.is(structure.obj)) continue
const unitTransforms = this.getUnitTransforms(structure.obj)
const d = structure.obj.data.boundary.sphere.radius * (p / 100)
if (!this.updatedUnitTransforms.has(structure.obj)) {
for (let i = 0, il = structure.obj.data.units.length; i < il; ++i) {
const u = structure.obj.data.units[i]
Vec3.transformMat4(this.centerVec, u.lookup3d.boundary.sphere.center, u.conformation.operator.matrix)
Vec3.sub(this.transVec, this.centerVec, structure.obj.data.boundary.sphere.center)
Vec3.setMagnitude(this.transVec, this.transVec, d)
Mat4.fromTranslation(this.animMat, this.transVec)
unitTransforms.setTransform(this.animMat, u)
}
this.updatedUnitTransforms.add(structure.obj)
}
r.obj.data.repr.setState({ unitTransforms })
this.ctx.canvas3d.add(r.obj.data.repr)
}
this.ctx.canvas3d.requestDraw(true)
}
animateExplode(play: boolean) {
if (play) {
const animateExplode = (t: number) => {
this.explode((Math.sin(t * 0.001) + 1) * 50)
this.explodeAnimHandle = requestAnimationFrame(animateExplode)
}
this.explodeAnimHandle = requestAnimationFrame(animateExplode)
} else {
cancelAnimationFrame(this.explodeAnimHandle)
}
}
register(): void { }
update(p: StructureAnimationProps) {
let updated = !PD.areEqual(StructureAnimationParams, this.params, p)
if (this.params.rotate !== p.rotate) {
this.params.rotate = p.rotate
this.animateRotate(this.params.rotate)
}
if (this.params.explode !== p.explode) {
this.params.explode = p.explode
this.animateExplode(this.params.explode)
}
if (this.params.rotateValue !== p.rotateValue) {
this.params.rotateValue = p.rotateValue
this.rotate(degToRad(this.params.rotateValue))
}
if (this.params.explodeValue !== p.explodeValue) {
this.params.explodeValue = p.explodeValue
this.explode(this.params.explodeValue)
}
return updated;
}
unregister() {
cancelAnimationFrame(this.rotateAnimHandle)
cancelAnimationFrame(this.explodeAnimHandle)
}
},
params: () => StructureAnimationParams
});
//
function getRootStructure(root: StateObjectCell, state: State) {
return state.select(StateSelection.Generators.byValue(root).rootOfType([PluginStateObject.Molecule.Structure]))[0];
}
\ No newline at end of file
......@@ -12,7 +12,7 @@ import * as ReactDOM from 'react-dom';
import { PluginSpec } from './spec';
import { StateTransforms } from './state/transforms';
import { PluginBehaviors } from './behavior';
import { AnimateModelIndex, AnimateAssemblyUnwind } from './state/animation/built-in';
import { AnimateModelIndex, AnimateAssemblyUnwind, AnimateUnitsExplode } from './state/animation/built-in';
import { StateActions } from './state/actions';
import { InitVolumeStreaming, BoxifyVolumeStreaming, CreateVolumeStreamingBehavior } from './behavior/dynamic/volume-streaming/transformers';
import { StructureRepresentationInteraction } from './behavior/dynamic/selection/structure-representation-interaction';
......@@ -58,7 +58,6 @@ export const DefaultPluginSpec: PluginSpec = {
PluginSpec.Behavior(PluginBehaviors.Representation.SelectLoci),
PluginSpec.Behavior(PluginBehaviors.Representation.DefaultLociLabelProvider),
PluginSpec.Behavior(PluginBehaviors.Camera.FocusLociOnSelect, { minRadius: 20, extraRadius: 4 }),
PluginSpec.Behavior(PluginBehaviors.Animation.StructureAnimation, { rotate: false, rotateValue: 0, explode: false, explodeValue: 0 }),
// PluginSpec.Behavior(PluginBehaviors.Labels.SceneLabels),
PluginSpec.Behavior(PluginBehaviors.CustomProps.PDBeStructureQualityReport, { autoAttach: true }),
PluginSpec.Behavior(PluginBehaviors.CustomProps.RCSBAssemblySymmetry, { autoAttach: true }),
......@@ -66,7 +65,8 @@ export const DefaultPluginSpec: PluginSpec = {
],
animations: [
AnimateModelIndex,
AnimateAssemblyUnwind
AnimateAssemblyUnwind,
AnimateUnitsExplode,
]
}
......
......@@ -111,6 +111,38 @@ export const AnimateAssemblyUnwind = PluginStateAnimation.create({
await PluginCommands.State.Update.dispatch(ctx.plugin, { state, tree: update, options: { doNotLogTiming: true } });
return { kind: 'next', state: { t: newTime } };
}
})
export const AnimateUnitsExplode = PluginStateAnimation.create({
name: 'built-in.animate-units-explode',
display: { name: 'Explode Units' },
params: () => ({
durationInMs: PD.Numeric(3000, { min: 100, max: 10000, step: 100})
}),
initialState: () => ({ t: 0 }),
async apply(animState, t, ctx) {
const state = ctx.plugin.state.dataState;
const anims = state.selectQ(q => q.rootsOfType(PluginStateObject.Molecule.Structure.Representation3DState)
.filter(c => c.transform.transformer === StateTransforms.Representation.ExplodeStructureRepresentation3D));
if (anims.length === 0) {
// nothing more to do here
return { kind: 'finished' };
}
const update = state.build();
const d = (t.current - t.lastApplied) / ctx.params.durationInMs;
const newTime = (animState.t + d) % 1;
for (const m of anims) {
update.to(m.transform.ref).update(StateTransforms.Representation.ExplodeStructureRepresentation3D, _ => ({ t: newTime }));
}
await PluginCommands.State.Update.dispatch(ctx.plugin, { state, tree: update, options: { doNotLogTiming: true } });
return { kind: 'next', state: { t: newTime } };
}
})
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment