diff --git a/src/mol-plugin/behavior/dynamic/animation.ts b/src/mol-plugin/behavior/dynamic/animation.ts index e79562ce6dbd3399cdd0fe101696407b58b12b8a..9216f827a91a048cdf036c13a75b6c0ac203cdf5 100644 --- a/src/mol-plugin/behavior/dynamic/animation.ts +++ b/src/mol-plugin/behavior/dynamic/animation.ts @@ -14,10 +14,16 @@ import { StateSelection } from 'mol-state/state/selection'; import { StateObjectCell, State } from 'mol-state'; const StructureAnimationParams = { - rotate: PD.Boolean(false) + rotate: PD.Boolean(false), + explode: PD.Boolean(false) } type StructureAnimationProps = PD.Values<typeof StructureAnimationParams> +function getStructure(root: StateObjectCell, state: State) { + const parent = StateSelection.findAncestorOfType(state.tree, state.cells, root.transform.ref, [PluginStateObject.Molecule.Structure]) + return parent && parent.obj ? parent.obj as PluginStateObject.Molecule.Structure : undefined +} + function getRootStructure(root: StateObjectCell, state: State) { let parent: StateObjectCell | undefined while (true) { @@ -29,8 +35,7 @@ function getRootStructure(root: StateObjectCell, state: State) { break } } - if (!parent || !parent.obj) return - return parent.obj as PluginStateObject.Molecule.Structure + return parent && parent.obj ? parent.obj as PluginStateObject.Molecule.Structure : undefined } // TODO this is just for testing purposes @@ -41,11 +46,12 @@ export const StructureAnimation = PluginBehavior.create<StructureAnimationProps> private tmpMat = Mat4.identity() private rotMat = Mat4.identity() private transMat = Mat4.identity() - private rotAnimMat = Mat4.identity() + private animMat = Mat4.identity() private transVec = Vec3.zero() private rotVec = Vec3.create(0, 1, 0) private rotateAnimHandle = -1 + private explodeAnimHandle = -1 constructor(protected ctx: PluginContext, protected params: StructureAnimationProps) { super(ctx, params) @@ -66,13 +72,13 @@ export const StructureAnimation = PluginBehavior.create<StructureAnimationProps> Vec3.negate(this.transVec, Vec3.copy(this.transVec, structure.data.boundary.sphere.center)) Mat4.fromTranslation(this.transMat, this.transVec) - Mat4.mul(this.rotAnimMat, this.rotMat, this.transMat) + Mat4.mul(this.animMat, this.rotMat, this.transMat) Vec3.copy(this.transVec, structure.data.boundary.sphere.center) Mat4.fromTranslation(this.transMat, this.transVec) - Mat4.mul(this.rotAnimMat, this.transMat, this.rotAnimMat) + Mat4.mul(this.animMat, this.transMat, this.animMat) - r.obj.data.setState({ transform: this.rotAnimMat }) + r.obj.data.setState({ transform: this.animMat }) this.ctx.canvas3d.add(r.obj.data) this.ctx.canvas3d.requestDraw(true) } @@ -84,19 +90,53 @@ export const StructureAnimation = PluginBehavior.create<StructureAnimationProps> } } + explode(play: boolean) { + if (play) { + const state = this.ctx.state.dataState + const reprs = state.select(q => q.rootsOfType(PluginStateObject.Molecule.Representation3D)); + const explode = (t: number) => { + const d = (Math.sin(t * 0.001) + 1) * 5 + for (const r of reprs) { + if (!SO.isRepresentation3D(r.obj)) return + const structure = getStructure(r, state) + if (!structure) continue + const rootStructure = getRootStructure(r, state) + if (!rootStructure) continue + + Vec3.sub(this.transVec, structure.data.boundary.sphere.center, rootStructure.data.boundary.sphere.center) + Vec3.setMagnitude(this.transVec, this.transVec, d) + Mat4.fromTranslation(this.animMat, this.transVec) + + r.obj.data.setState({ transform: this.animMat }) + this.ctx.canvas3d.add(r.obj.data) + this.ctx.canvas3d.requestDraw(true) + } + this.explodeAnimHandle = requestAnimationFrame(explode) + } + this.explodeAnimHandle = requestAnimationFrame(explode) + } else { + cancelAnimationFrame(this.explodeAnimHandle) + } + } + register(): void { } update(p: StructureAnimationProps) { - let updated = this.params.rotate !== p.rotate - this.params.rotate = p.rotate - if (updated) { + let updated = PD.areEqual(StructureAnimationParams, this.params, p) + if (this.params.rotate !== p.rotate) { + this.params.rotate = p.rotate this.rotate(this.params.rotate) } + if (this.params.explode !== p.explode) { + this.params.explode = p.explode + this.explode(this.params.explode) + } return updated; } unregister() { cancelAnimationFrame(this.rotateAnimHandle) + cancelAnimationFrame(this.explodeAnimHandle) } }, params: () => StructureAnimationParams diff --git a/src/mol-plugin/index.ts b/src/mol-plugin/index.ts index a22b53ddb3b17f0e3ffbb1d2ecb591f09c0d74c5..e0872207471cf72d6fd175c79f7c234965b5e5ec 100644 --- a/src/mol-plugin/index.ts +++ b/src/mol-plugin/index.ts @@ -37,7 +37,7 @@ const DefaultSpec: 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 }), + PluginSpec.Behavior(PluginBehaviors.Animation.StructureAnimation, { rotate: false, explode: false }), PluginSpec.Behavior(PluginBehaviors.CustomProps.PDBeStructureQualityReport, { autoAttach: true }), PluginSpec.Behavior(PluginBehaviors.CustomProps.RCSBAssemblySymmetry, { autoAttach: true }), ]