Browse Source

mol-model: Fast unit conformation comparison

David Sehnal 4 years ago
parent
commit
d44bb6c908

+ 2 - 1
src/mol-math/geometry/symmetry-operator.ts

@@ -151,6 +151,7 @@ namespace SymmetryOperator {
 
     export interface CoordinateMapper<T extends number> { (index: T, slot: Vec3): Vec3 }
     export interface ArrayMapping<T extends number> {
+        readonly coordinates: Coordinates,
         readonly operator: SymmetryOperator,
         readonly invariantPosition: CoordinateMapper<T>,
         readonly position: CoordinateMapper<T>,
@@ -166,7 +167,7 @@ namespace SymmetryOperator {
         const invariantPosition = SymmetryOperator.createCoordinateMapper(SymmetryOperator.Default, coords);
         const position = operator.isIdentity ? invariantPosition : SymmetryOperator.createCoordinateMapper(operator, coords);
         const { x, y, z } = createProjections(operator, coords);
-        return { operator, invariantPosition, position, x, y, z, r: radius ? radius : _zeroRadius };
+        return { operator, coordinates: coords, invariantPosition, position, x, y, z, r: radius ? radius : _zeroRadius };
     }
 
     export function createCoordinateMapper<T extends number>(t: SymmetryOperator, coords: Coordinates): CoordinateMapper<T> {

+ 19 - 33
src/mol-model/structure/structure/unit.ts

@@ -21,7 +21,7 @@ import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
 import { PrincipalAxes } from '../../../mol-math/linear-algebra/matrix/principal-axes';
 import { getPrincipalAxes } from './util/principal-axes';
 import { Boundary, getBoundary } from '../../../mol-math/geometry/boundary';
-import { Vec3 } from '../../../mol-math/linear-algebra';
+import { Mat4 } from '../../../mol-math/linear-algebra';
 
 /**
  * A building block of a structure that corresponds to an atomic or
@@ -106,23 +106,6 @@ namespace Unit {
         return hash2(u.invariantId, SortedArray.hashCode(u.elements));
     }
 
-    const tmpV = Vec3();
-
-    export function positionHash(unit: Unit) {
-        let hval = 0x811c9dc5;
-        const pos = unit.conformation.invariantPosition;
-        for (let i = 0, il = unit.elements.length; i < il; ++i) {
-            pos(unit.elements[i], tmpV);
-            hval ^= tmpV[0];
-            hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
-            hval ^= tmpV[1];
-            hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
-            hval ^= tmpV[2];
-            hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
-        }
-        return hval >>> 0;
-    }
-
     export type Traits = BitFlags<Trait>
     export const enum Trait {
         None = 0x0,
@@ -164,7 +147,6 @@ namespace Unit {
         principalAxes?: PrincipalAxes
         polymerElements?: SortedArray<ElementIndex>
         gapElements?: SortedArray<ElementIndex>
-        positionHash?: number
     }
 
 
@@ -222,7 +204,7 @@ namespace Unit {
         }
 
         remapModel(model: Model) {
-            const props = { ...this.props, positionHash: undefined };
+            const props = { ...this.props };
             const conformation = this.model.atomicConformation !== model.atomicConformation
                 ? SymmetryOperator.createMapping(this.conformation.operator, model.atomicConformation)
                 : this.conformation;
@@ -249,12 +231,6 @@ namespace Unit {
             return this.props.principalAxes;
         }
 
-        get positionHash() {
-            if (this.props.positionHash) return this.props.positionHash;
-            this.props.positionHash = Unit.positionHash(this);
-            return this.props.positionHash;
-        }
-
         get bonds() {
             if (this.props.bonds) return this.props.bonds;
             this.props.bonds = computeIntraUnitBonds(this);
@@ -366,7 +342,7 @@ namespace Unit {
         }
 
         remapModel(model: Model): Unit.Spheres | Unit.Gaussians {
-            const props = { ...this.props, positionHash: undefined };
+            const props = { ...this.props };
             let conformation: SymmetryOperator.ArrayMapping<ElementIndex>;
             if (this.kind === Kind.Spheres) {
                 conformation = this.model.coarseConformation.spheres !== model.coarseConformation.spheres
@@ -404,12 +380,6 @@ namespace Unit {
             return this.props.principalAxes;
         }
 
-        get positionHash() {
-            if (this.props.positionHash) return this.props.positionHash;
-            this.props.positionHash = Unit.positionHash(this as Unit.Spheres | Unit.Gaussians); // TODO get rid of casting
-            return this.props.positionHash;
-        }
-
         get polymerElements() {
             if (this.props.polymerElements) return this.props.polymerElements;
             this.props.polymerElements = getCoarsePolymerElements(this as Unit.Spheres | Unit.Gaussians); // TODO get rid of casting
@@ -458,6 +428,22 @@ namespace Unit {
     export function areSameChainOperatorGroup(a: Unit, b: Unit) {
         return a.chainGroupId === b.chainGroupId && a.conformation.operator.name === b.conformation.operator.name;
     }
+
+    export function areAreConformationsEquivalent(a: Unit, b: Unit) {
+        if (a.elements.length !== b.elements.length) return false;
+        if (!Mat4.areEqual(a.conformation.operator.matrix, b.conformation.operator.matrix, 1e-6)) return false;
+
+        const xs = a.elements, ys = b.elements;
+        const { x: xa, y: ya, z: za } = a.conformation.coordinates;
+        const { x: xb, y: yb, z: zb } = b.conformation.coordinates;
+
+        for (let i = 0, _i = xs.length; i < _i; i++) {
+            const u = xs[i], v = ys[i];
+            if (xa[u] !== xb[v] || ya[u] !== yb[v] || za[u] !== zb[v]) return false;
+        }
+
+        return true;
+    }
 }
 
 export default Unit;

+ 1 - 1
src/mol-repr/structure/units-visual.ts

@@ -130,7 +130,7 @@ export function UnitsVisual<G extends Geometry, P extends StructureParams & Geom
         if (Unit.conformationId(newUnit) !== Unit.conformationId(currentUnit)) {
             // console.log('new conformation');
             updateState.updateTransform = true;
-            if (newUnit.positionHash !== currentUnit.positionHash) {
+            if (!Unit.areAreConformationsEquivalent(newUnit, currentUnit)) {
                 // console.log('new position');
                 updateState.createGeometry = true;
             }