Browse Source

Added "repr3d explosion behavior" and StateObjectTracker helper class

David Sehnal 6 years ago
parent
commit
217691de87

+ 89 - 4
src/mol-plugin/behavior/dynamic/representation.ts

@@ -4,11 +4,17 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { PluginBehavior } from '../behavior';
-import { EmptyLoci, Loci } from 'mol-model/loci';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
-import { labelFirst } from 'mol-theme/label';
+import { Mat4, Vec3 } from 'mol-math/linear-algebra';
+import { EmptyLoci, Loci } from 'mol-model/loci';
+import { StructureUnitTransforms } from 'mol-model/structure/structure/util/unit-transforms';
 import { PluginContext } from 'mol-plugin/context';
+import { PluginStateObject } from 'mol-plugin/state/objects';
+import { StateObjectTracker } from 'mol-state';
+import { StateSelection } from 'mol-state/state/selection';
+import { labelFirst } from 'mol-theme/label';
+import { ParamDefinition as PD } from 'mol-util/param-definition';
+import { PluginBehavior } from '../behavior';
 
 export const HighlightLoci = PluginBehavior.create({
     name: 'representation-highlight-loci',
@@ -61,4 +67,83 @@ export const DefaultLociLabelProvider = PluginBehavior.create({
         constructor(protected ctx: PluginContext) { }
     },
     display: { name: 'Provide Default Loci Label', group: 'Representation' }
-});
+});
+
+
+export namespace ExplodeRepresentation3D {
+    export const Params = {
+        t: PD.Numeric(0, { min: 0, max: 1, step: 0.01 })
+    }
+    export type Params = PD.Values<typeof Params>
+
+    export class Behavior implements PluginBehavior<Params> {
+        private currentT = 0;
+        private repr: StateObjectTracker<PluginStateObject.Molecule.Representation3D>;
+        private structure: StateObjectTracker<PluginStateObject.Molecule.Structure>;
+        private transforms: StructureUnitTransforms;
+
+        private updateData() {
+            const reprUpdated = this.repr.update();
+            const strUpdated = this.structure.update();
+            if (strUpdated && this.structure.data) {
+                this.transforms = new StructureUnitTransforms(this.structure.data);
+            }
+            return reprUpdated || strUpdated;
+        }
+
+        register(ref: string): void {
+            this.repr.setQuery(StateSelection.Generators.byRef(ref).ancestorOfType([PluginStateObject.Molecule.Representation3D]));
+            this.structure.setQuery(StateSelection.Generators.byRef(ref).rootOfType([PluginStateObject.Molecule.Structure]));
+            this.update(this.params);
+        }
+
+        private centerVec = Vec3.zero();
+        private transVec = Vec3.zero();
+        private transMat = Mat4.zero();
+
+        update(params: Params): boolean | Promise<boolean> {
+            if (!this.updateData() && params.t === this.currentT) return false;
+            this.currentT = params.t;
+            if (!this.structure.data || !this.repr.data) return true;
+
+            const structure = this.structure.data;
+            const boundary = structure.boundary.sphere;
+            const d = boundary.radius * params.t;
+
+            for (let i = 0, _i = structure.units.length; i < _i; i++) {
+                const u = structure.units[i];
+
+                Vec3.transformMat4(this.centerVec, u.lookup3d.boundary.sphere.center, u.conformation.operator.matrix);
+                Vec3.sub(this.transVec, this.centerVec, boundary.center);
+                Vec3.setMagnitude(this.transVec, this.transVec, d);
+                Mat4.fromTranslation(this.transMat, this.transVec)
+
+                this.transforms.setTransform(this.transMat, u);
+            }
+
+            // TODO: should be be "auto updated"?
+            // perhaps have Representation3D.setState(state, autoSync = false)?
+
+            // TODO: where to handle unitTransforms composition?
+            // Manually or inside the representation? "inside" would better compose with future additions.
+            this.repr.data.setState({ unitTransforms: this.transforms });
+            this.ctx.canvas3d.add(this.repr.data);
+            this.ctx.canvas3d.requestDraw(true);
+
+            return true;
+        }
+
+        unregister(): void {
+            this.update({ t: 0 })
+            this.repr.cell = void 0;
+            this.structure.cell = void 0;
+        }
+
+        constructor(private ctx: PluginContext, private params: Params) {
+            this.repr = new StateObjectTracker(ctx.state.dataState);
+            this.structure = new StateObjectTracker(ctx.state.dataState);
+        }
+    }
+
+    export class Obj extends PluginStateObject.CreateBehavior<Behavior>({ name: 'Explode Representation3D Behavior' }) { }
+}

+ 2 - 1
src/mol-plugin/index.ts

@@ -30,7 +30,8 @@ const DefaultSpec: PluginSpec = {
         PluginSpec.Action(StateTransforms.Model.StructureAssemblyFromModel),
         PluginSpec.Action(StateTransforms.Model.StructureFromModel),
         PluginSpec.Action(StateTransforms.Model.ModelFromTrajectory),
-        PluginSpec.Action(StateTransforms.Representation.StructureRepresentation3D)
+        PluginSpec.Action(StateTransforms.Representation.StructureRepresentation3D),
+        PluginSpec.Action(StateTransforms.Representation.ExplodeStructureRepresentation3D)
     ],
     behaviors: [
         PluginSpec.Behavior(PluginBehaviors.Representation.HighlightLoci),

+ 25 - 0
src/mol-plugin/state/transforms/representation.ts

@@ -15,6 +15,7 @@ import { createTheme } from 'mol-theme/theme';
 import { BuiltInStructureRepresentationsName } from 'mol-repr/structure/registry';
 import { Structure } from 'mol-model/structure';
 import { StructureParams } from 'mol-repr/structure/representation';
+import { ExplodeRepresentation3D } from 'mol-plugin/behavior/dynamic/representation';
 
 export namespace StructureRepresentation3DHelpers {
     export function getDefaultParams(ctx: PluginContext, name: BuiltInStructureRepresentationsName, structure: Structure, structureParams?: Partial<PD.Values<StructureParams>>): Transformer.Params<StructureRepresentation3D> {
@@ -95,4 +96,28 @@ const StructureRepresentation3D = PluginStateTransform.BuiltIn({
             return Transformer.UpdateResult.Updated;
         });
     }
+});
+
+export { ExplodeStructureRepresentation3D }
+type ExplodeStructureRepresentation3D = typeof ExplodeStructureRepresentation3D
+const ExplodeStructureRepresentation3D = PluginStateTransform.BuiltIn({
+    name: 'explode-structure-representation-3d',
+    display: 'Explode 3D Representation',
+    from: SO.Molecule.Representation3D,
+    to: ExplodeRepresentation3D.Obj,
+    params: ExplodeRepresentation3D.Params
+})({
+    canAutoUpdate() {
+        return true;
+    },
+    apply({ params }, plugin: PluginContext) {
+        return new ExplodeRepresentation3D.Obj(new ExplodeRepresentation3D.Behavior(plugin, params), { label: `Explosion T = ${params.t.toFixed(2)}` });
+    },
+    update({ b, newParams }) {
+        return Task.create('Update Explosion', async () => {
+            const updated = await b.data.update(newParams);
+            b.label = `Explosion T = ${newParams.t.toFixed(2)}`;
+            return updated ? Transformer.UpdateResult.Updated : Transformer.UpdateResult.Unchanged;
+        });
+    }
 });

+ 26 - 0
src/mol-state/object.ts

@@ -7,6 +7,8 @@
 import { UUID } from 'mol-util';
 import { Transform } from './transform';
 import { ParamDefinition } from 'mol-util/param-definition';
+import { State } from './state';
+import { StateSelection } from './state/selection';
 
 export { StateObject, StateObjectCell }
 
@@ -89,4 +91,28 @@ namespace StateObjectCell {
         if (typeof b.isHidden !== 'undefined' && a.isHidden !== b.isHidden) return true;
         return false;
     }
+}
+
+// TODO: improve the API?
+export class StateObjectTracker<T extends StateObject> {
+    private query: StateSelection.Query;
+    private version: string = '';
+    cell: StateObjectCell | undefined;
+    data: T['data'] | undefined;
+
+    setQuery(sel: StateSelection.Selector) {
+        this.query = StateSelection.compile(sel);
+    }
+
+    update() {
+        const cell = this.state.query(this.query)[0];
+        const version = cell ? cell.transform.version : void 0;
+        const changed = this.cell !== cell || this.version !== version;
+        this.cell = cell;
+        this.version = version || '';
+        this.data = cell.obj ? cell.obj.data as T : void 0 as any;
+        return changed;
+    }
+
+    constructor(private state: State) { }
 }