Sebastian Bittrich 1 éve
szülő
commit
505b04c92d

+ 5 - 0
CHANGELOG.md

@@ -6,8 +6,13 @@ Note that since we don't clearly distinguish between a public and private interf
 
 ## [Unreleased]
 
+- Make operators in `IndexPairBonds` a directed property
+- Remove erroneous bounding-box overlap test in `Structure.eachUnitPair`
+- Fix `EdgeBuilder.addNextEdge` for loop edges
 - Optimize inter unit bond compute
 - Ensure consistent state for volume representation (#210)
+- Improve SSAO for thin geometry (e.g. lines)
+- Add snapshot support for structure selections
 
 ## [v3.35.0] - 2023-05-14
 

+ 1 - 1
src/mol-gl/shader/ssao.frag.ts

@@ -114,7 +114,7 @@ void main(void) {
     vec2 selfPackedDepth = packUnitIntervalToRG(selfDepth);
 
     if (isBackground(selfDepth)) {
-        gl_FragColor = vec4(packUnitIntervalToRG(0.0), selfPackedDepth);
+        gl_FragColor = vec4(packUnitIntervalToRG(1.0), selfPackedDepth);
         return;
     }
 

+ 26 - 20
src/mol-math/graph/int-adjacency-graph.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -49,7 +49,7 @@ export interface IntAdjacencyGraph<VertexIndex extends number, EdgeProps extends
 export namespace IntAdjacencyGraph {
     export type EdgePropsBase = { [name: string]: ArrayLike<any> }
 
-    export function areEqual<I extends number, P extends IntAdjacencyGraph.EdgePropsBase>(a: IntAdjacencyGraph<I, P>, b: IntAdjacencyGraph<I, P>) {
+    export function areEqual<VertexIndex extends number, EdgeProps extends IntAdjacencyGraph.EdgePropsBase>(a: IntAdjacencyGraph<VertexIndex, EdgeProps>, b: IntAdjacencyGraph<VertexIndex, EdgeProps>) {
         if (a === b) return true;
 
         if (a.vertexCount !== b.vertexCount || a.edgeCount !== b.edgeCount) return false;
@@ -149,12 +149,11 @@ export namespace IntAdjacencyGraph {
             const a = this.xs[this.current], b = this.ys[this.current];
 
             const oa = this.offsets[a] + this.bucketFill[a];
-            const ob = this.offsets[b] + this.bucketFill[b];
-
             this.a[oa] = a;
             this.b[oa] = b;
             this.bucketFill[a]++;
 
+            const ob = this.offsets[b] + this.bucketFill[b];
             this.a[ob] = b;
             this.b[ob] = a;
             this.bucketFill[b]++;
@@ -176,6 +175,13 @@ export namespace IntAdjacencyGraph {
             prop[this.curB] = value;
         }
 
+        assignDirectedProperty<T>(propA: { [i: number]: T }, valueA: T, propB: { [i: number]: T }, valueB: T) {
+            propA[this.curA] = valueA;
+            propA[this.curB] = valueB;
+            propB[this.curB] = valueA;
+            propB[this.curA] = valueB;
+        }
+
         constructor(public vertexCount: number, public xs: ArrayLike<VertexIndex>, public ys: ArrayLike<VertexIndex>) {
             this.edgeCount = xs.length;
             this.offsets = new Int32Array(this.vertexCount + 1);
@@ -206,11 +212,11 @@ export namespace IntAdjacencyGraph {
         edgeCount: number;
         /** the size of the A and B arrays */
         slotCount: number;
-        a: Int32Array;
-        b: Int32Array;
+        a: AssignableArrayLike<VertexIndex>;
+        b: AssignableArrayLike<VertexIndex>;
 
-        createGraph<EdgeProps extends IntAdjacencyGraph.EdgePropsBase>(edgeProps: EdgeProps) {
-            return create(this.offsets, this.a, this.b, this.edgeCount, edgeProps);
+        createGraph<EdgeProps extends IntAdjacencyGraph.EdgePropsBase, Props>(edgeProps: EdgeProps, props?: Props) {
+            return create<VertexIndex, EdgeProps, Props>(this.offsets, this.a, this.b, this.edgeCount, edgeProps, props);
         }
 
         /**
@@ -261,8 +267,8 @@ export namespace IntAdjacencyGraph {
             }
             this.offsets[this.vertexCount] = offset;
             this.slotCount = offset;
-            this.a = new Int32Array(offset);
-            this.b = new Int32Array(offset);
+            this.a = new Int32Array(offset) as unknown as AssignableArrayLike<VertexIndex>;
+            this.b = new Int32Array(offset) as unknown as AssignableArrayLike<VertexIndex>;
         }
     }
 
@@ -295,13 +301,13 @@ export namespace IntAdjacencyGraph {
         }
     }
 
-    export function fromVertexPairs<V extends number>(vertexCount: number, xs: V[], ys: V[]) {
+    export function fromVertexPairs<VertexIndex extends number>(vertexCount: number, xs: VertexIndex[], ys: VertexIndex[]) {
         const graphBuilder = new IntAdjacencyGraph.EdgeBuilder(vertexCount, xs, ys);
         graphBuilder.addAllEdges();
         return graphBuilder.createGraph({});
     }
 
-    export function induceByVertices<V extends number, P extends IntAdjacencyGraph.EdgePropsBase>(graph: IntAdjacencyGraph<V, P>, vertexIndices: ArrayLike<number>): IntAdjacencyGraph<V, P> {
+    export function induceByVertices<VertexIndex extends number, EdgeProps extends IntAdjacencyGraph.EdgePropsBase, Props>(graph: IntAdjacencyGraph<VertexIndex, EdgeProps>, vertexIndices: ArrayLike<number>, props?: Props): IntAdjacencyGraph<VertexIndex, EdgeProps> {
         const { b, offset, vertexCount, edgeProps } = graph;
         const vertexMap = new Int32Array(vertexCount);
         for (let i = 0, _i = vertexIndices.length; i < _i; i++) vertexMap[vertexIndices[i]] = i + 1;
@@ -316,8 +322,8 @@ export namespace IntAdjacencyGraph {
 
         const newOffsets = new Int32Array(vertexIndices.length + 1);
         const edgeIndices = new Int32Array(2 * newEdgeCount);
-        const newA = new Int32Array(2 * newEdgeCount) as unknown as AssignableArrayLike<V>;
-        const newB = new Int32Array(2 * newEdgeCount) as unknown as AssignableArrayLike<V>;
+        const newA = new Int32Array(2 * newEdgeCount) as unknown as AssignableArrayLike<VertexIndex>;
+        const newB = new Int32Array(2 * newEdgeCount) as unknown as AssignableArrayLike<VertexIndex>;
         let eo = 0, vo = 0;
         for (let i = 0; i < vertexCount; i++) {
             if (vertexMap[i] === 0) continue;
@@ -326,20 +332,20 @@ export namespace IntAdjacencyGraph {
                 const bb = vertexMap[b[j]];
                 if (bb === 0) continue;
 
-                newA[eo] = aa as V;
-                newB[eo] = bb - 1 as V;
+                newA[eo] = aa as VertexIndex;
+                newB[eo] = bb - 1 as VertexIndex;
                 edgeIndices[eo] = j;
                 eo++;
             }
             newOffsets[++vo] = eo;
         }
 
-        const newEdgeProps = {} as P;
-        for (const key of Object.keys(edgeProps) as (keyof P)[]) {
-            newEdgeProps[key] = arrayPickIndices(edgeProps[key], edgeIndices) as P[keyof P];
+        const newEdgeProps = {} as EdgeProps;
+        for (const key of Object.keys(edgeProps) as (keyof EdgeProps)[]) {
+            newEdgeProps[key] = arrayPickIndices(edgeProps[key], edgeIndices) as EdgeProps[keyof EdgeProps];
         }
 
-        return create(newOffsets, newA, newB, newEdgeCount, newEdgeProps);
+        return create<VertexIndex, EdgeProps, Props>(newOffsets, newA, newB, newEdgeCount, newEdgeProps, props);
     }
 
     export function connectedComponents(graph: IntAdjacencyGraph<any, any>): { componentCount: number, componentIndex: Int32Array } {

+ 5 - 3
src/mol-model-formats/structure/property/bonds/index-pair.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019-2022 Mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2023 Mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author David Sehnal <david.sehnal@gmail.com>
@@ -34,8 +34,10 @@ function getGraph(indexA: ArrayLike<ElementIndex>, indexB: ArrayLike<ElementInde
     for (let i = 0, _i = builder.edgeCount; i < _i; i++) {
         builder.addNextEdge();
         builder.assignProperty(key, props.key ? props.key[i] : -1);
-        builder.assignProperty(operatorA, props.operatorA ? props.operatorA[i] : -1);
-        builder.assignProperty(operatorB, props.operatorB ? props.operatorB[i] : -1);
+        builder.assignDirectedProperty(
+            operatorA, props.operatorA ? props.operatorA[i] : -1,
+            operatorB, props.operatorB ? props.operatorB[i] : -1
+        );
         builder.assignProperty(order, props.order ? props.order[i] : 1);
         builder.assignProperty(distance, props.distance ? props.distance[i] : -1);
         builder.assignProperty(flag, props.flag ? props.flag[i] : BondType.Flag.Covalent);

+ 10 - 15
src/mol-model/structure/structure/structure.ts

@@ -23,7 +23,7 @@ import { Carbohydrates } from './carbohydrates/data';
 import { computeCarbohydrates } from './carbohydrates/compute';
 import { Vec3, Mat4 } from '../../../mol-math/linear-algebra';
 import { idFactory } from '../../../mol-util/id-factory';
-import { Box3D, GridLookup3D } from '../../../mol-math/geometry';
+import { GridLookup3D } from '../../../mol-math/geometry';
 import { UUID } from '../../../mol-util';
 import { CustomProperties } from '../../custom-property';
 import { AtomicHierarchy } from '../model/properties/atomic';
@@ -1213,25 +1213,20 @@ namespace Structure {
 
         const lookup = structure.lookup3d;
         const imageCenter = Vec3();
-        const bbox = Box3D();
-        const rvec = Vec3.create(maxRadius, maxRadius, maxRadius);
 
-        for (const unit of structure.units) {
-            if (!validUnit(unit)) continue;
+        for (const unitA of structure.units) {
+            if (!validUnit(unitA)) continue;
 
-            const bs = unit.boundary.sphere;
-            Box3D.expand(bbox, unit.boundary.box, rvec);
-            Vec3.transformMat4(imageCenter, bs.center, unit.conformation.operator.matrix);
+            const bs = unitA.boundary.sphere;
+            Vec3.transformMat4(imageCenter, bs.center, unitA.conformation.operator.matrix);
             const closeUnits = lookup.findUnitIndices(imageCenter[0], imageCenter[1], imageCenter[2], bs.radius + maxRadius);
             for (let i = 0; i < closeUnits.count; i++) {
-                const other = structure.units[closeUnits.indices[i]];
-                if (unit.id >= other.id) continue;
-
-                if (other.elements.length > 3 && !Box3D.overlaps(bbox, other.boundary.box)) continue;
-                if (!validUnit(other) || !validUnitPair(unit, other)) continue;
+                const unitB = structure.units[closeUnits.indices[i]];
+                if (unitA.id >= unitB.id) continue;
+                if (!validUnit(unitB) || !validUnitPair(unitA, unitB)) continue;
 
-                if (other.elements.length >= unit.elements.length) callback(unit, other);
-                else callback(other, unit);
+                if (unitB.elements.length >= unitA.elements.length) callback(unitA, unitB);
+                else callback(unitB, unitA);
             }
         }
     }

+ 34 - 2
src/mol-plugin-state/manager/structure/selection.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -12,7 +12,7 @@ import { PrincipalAxes } from '../../../mol-math/linear-algebra/matrix/principal
 import { EmptyLoci, Loci } from '../../../mol-model/loci';
 import { QueryContext, Structure, StructureElement, StructureQuery, StructureSelection } from '../../../mol-model/structure';
 import { PluginContext } from '../../../mol-plugin/context';
-import { StateObjectRef } from '../../../mol-state';
+import { StateObjectRef, StateSelection } from '../../../mol-state';
 import { Task } from '../../../mol-task';
 import { structureElementStatsLabel } from '../../../mol-theme/label';
 import { arrayRemoveAtInPlace } from '../../../mol-util/array';
@@ -35,6 +35,13 @@ const HISTORY_CAPACITY = 24;
 
 export type StructureSelectionModifier = 'add' | 'remove' | 'intersect' | 'set'
 
+export type StructureSelectionSnapshot = {
+    entries: {
+        ref: string
+        bundle: StructureElement.Bundle
+    }[]
+}
+
 export class StructureSelectionManager extends StatefulPluginComponent<StructureSelectionManagerState> {
     readonly events = {
         changed: this.ev<undefined>(),
@@ -484,6 +491,31 @@ export class StructureSelectionManager extends StatefulPluginComponent<Structure
         }
     }
 
+    getSnapshot(): StructureSelectionSnapshot {
+        const entries: StructureSelectionSnapshot['entries'] = [];
+
+        this.entries.forEach((entry, ref) => {
+            entries.push({
+                ref,
+                bundle: StructureElement.Bundle.fromLoci(entry.selection)
+            });
+        });
+
+        return { entries };
+    }
+
+    setSnapshot(snapshot: StructureSelectionSnapshot) {
+        this.entries.clear();
+
+        for (const { ref, bundle } of snapshot.entries) {
+            const structure = this.plugin.state.data.select(StateSelection.Generators.byRef(ref))[0]?.obj?.data as Structure;
+            if (!structure) continue;
+
+            const loci = StructureElement.Bundle.toLoci(bundle, structure);
+            this.fromLoci('set', loci, false);
+        }
+    }
+
     constructor(private plugin: PluginContext) {
         super({ entries: new Map(), additionsHistory: [], stats: SelectionStats() });
 

+ 8 - 0
src/mol-plugin/state.ts

@@ -2,6 +2,7 @@
  * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
 import { State, StateTransform, StateTransformer } from '../mol-state';
@@ -21,6 +22,7 @@ import { PluginContext } from './context';
 import { PluginComponent } from '../mol-plugin-state/component';
 import { PluginConfig } from './config';
 import { StructureComponentManager } from '../mol-plugin-state/manager/structure/component';
+import { StructureSelectionSnapshot } from '../mol-plugin-state/manager/structure/selection';
 
 export { PluginState };
 
@@ -65,6 +67,7 @@ class PluginState extends PluginComponent {
             canvas3d: p.canvas3d ? { props: this.plugin.canvas3d?.props } : void 0,
             interactivity: p.interactivity ? { props: this.plugin.managers.interactivity.props } : void 0,
             structureFocus: this.plugin.managers.structure.focus.getSnapshot(),
+            structureSelection: p.structureSelection ? this.plugin.managers.structure.selection.getSnapshot() : void 0,
             structureComponentManager: p.componentManager ? {
                 options: this.plugin.managers.structure.component.state.options
             } : void 0,
@@ -89,6 +92,9 @@ class PluginState extends PluginComponent {
         if (snapshot.structureFocus) {
             this.plugin.managers.structure.focus.setSnapshot(snapshot.structureFocus);
         }
+        if (snapshot.structureSelection) {
+            this.plugin.managers.structure.selection.setSnapshot(snapshot.structureSelection);
+        }
         if (snapshot.animation) {
             this.animation.setSnapshot(snapshot.animation);
         }
@@ -146,6 +152,7 @@ namespace PluginState {
         durationInMs: PD.Numeric(1500, { min: 100, max: 15000, step: 100 }, { label: 'Duration in ms' }),
         data: PD.Boolean(true),
         behavior: PD.Boolean(false),
+        structureSelection: PD.Boolean(false),
         componentManager: PD.Boolean(true),
         animation: PD.Boolean(true),
         startAnimation: PD.Boolean(false),
@@ -181,6 +188,7 @@ namespace PluginState {
             props?: InteractivityManager.Props
         },
         structureFocus?: StructureFocusSnapshot,
+        structureSelection?: StructureSelectionSnapshot,
         structureComponentManager?: {
             options?: StructureComponentManager.Options
         },