Browse Source

gaussian surface visual improvements

- add structure.unitsSortedByVolume
- increase gaussian smoothness in coarse presets
- use slice area instead of volume to ensure reasonable resolution
- use largest units (by volume) for reasonable resolution calculation
Alexander Rose 4 years ago
parent
commit
bffdff6aad

+ 26 - 3
src/mol-model/structure/structure/structure.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2017-2021 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>
@@ -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 { GridLookup3D } from '../../../mol-math/geometry';
+import { Box3D, GridLookup3D } from '../../../mol-math/geometry';
 import { UUID } from '../../../mol-util';
 import { CustomProperties } from '../../custom-property';
 import { AtomicHierarchy } from '../model/properties/atomic';
@@ -50,6 +50,7 @@ class Structure {
         interUnitBonds?: InterUnitBonds,
         unitSymmetryGroups?: ReadonlyArray<Unit.SymmetryGroup>,
         unitSymmetryGroupsIndexMap?: IntMap<number>,
+        unitsSortedByVolume?: ReadonlyArray<Unit>;
         carbohydrates?: Carbohydrates,
         models?: ReadonlyArray<Model>,
         model?: Model,
@@ -257,6 +258,13 @@ class Structure {
         return this._props.unitSymmetryGroupsIndexMap;
     }
 
+    /** Array of all units in the structure, sorted by their boundary volume */
+    get unitsSortedByVolume(): ReadonlyArray<Unit> {
+        if (this._props.unitsSortedByVolume) return this._props.unitsSortedByVolume;
+        this._props.unitsSortedByVolume = getUnitsSortedByVolume(this);
+        return this._props.unitsSortedByVolume;
+    }
+
     get carbohydrates(): Carbohydrates {
         if (this._props.carbohydrates) return this._props.carbohydrates;
         this._props.carbohydrates = computeCarbohydrates(this);
@@ -419,7 +427,22 @@ class Structure {
     }
 }
 
-function cmpUnits(units: ArrayLike<Unit>, i: number, j: number) { return units[i].id - units[j].id; }
+function cmpUnits(units: ArrayLike<Unit>, i: number, j: number) {
+    return units[i].id - units[j].id;
+
+}
+function cmpUnitsVolume(units: ArrayLike<Unit>, i: number, j: number) {
+    return Box3D.volume(units[i].boundary.box) - Box3D.volume(units[i].boundary.box);
+}
+
+function getUnitsSortedByVolume(structure: Structure) {
+    const { units } = structure;
+    const unitsByVolume: Unit[] = [];
+    for (let i = 0, _i = units.length; i < _i; i++) {
+        unitsByVolume[i] = units[i];
+    }
+    return sort(unitsByVolume, 0, unitsByVolume.length, cmpUnitsVolume, arraySwap);
+}
 
 function getModels(s: Structure) {
     const { units } = s;

+ 4 - 4
src/mol-plugin-state/builder/structure/representation-preset.ts

@@ -209,7 +209,7 @@ const proteinAndNucleic = StructureRepresentationPresetProvider({
         };
         const gaussianProps = {
             radiusOffset: structure.isCoarseGrained ? 2 : 0,
-            smoothness: structure.isCoarseGrained ? 0.5 : 1.5,
+            smoothness: structure.isCoarseGrained ? 1.0 : 1.5,
         };
 
         const { update, builder, typeParams, symmetryColor } = reprBuilder(plugin, params, structure);
@@ -248,18 +248,18 @@ const coarseSurface = StructureRepresentationPresetProvider({
             Object.assign(gaussianProps, {
                 traceOnly: true,
                 radiusOffset: 2,
-                smoothness: 0.5,
+                smoothness: 1,
                 visuals: ['structure-gaussian-surface-mesh']
             });
         } else if(size === Structure.Size.Huge) {
             Object.assign(gaussianProps, {
                 radiusOffset: structure.isCoarseGrained ? 2 : 0,
-                smoothness: 0.5,
+                smoothness: 1,
             });
         } else if(structure.isCoarseGrained) {
             Object.assign(gaussianProps, {
                 radiusOffset: 2,
-                smoothness: 0.5,
+                smoothness: 1,
             });
         }
 

+ 5 - 3
src/mol-repr/structure/visual/util/common.ts

@@ -104,9 +104,11 @@ const DefaultMaxCells = 500_000_000;
 
 /** guard against overly high resolution for the given box size */
 export function ensureReasonableResolution<T>(box: Box3D, props: { resolution: number } & T, maxCells = DefaultMaxCells) {
-    const volume = Box3D.volume(box);
-    const approxCells = volume / props.resolution;
-    const resolution = approxCells > maxCells ? volume / maxCells : props.resolution;
+    const size = Box3D.size(Vec3(), box);
+    const maxArea = Math.floor(Math.cbrt(maxCells) * Math.cbrt(maxCells));
+    const area = size[0] * size[1];
+    const maxAreaCells = Math.ceil(area / props.resolution);
+    const resolution = maxAreaCells > maxArea ? area / maxArea : props.resolution;
     return { ...props, resolution };
 }
 

+ 10 - 3
src/mol-repr/structure/visual/util/gaussian.ts

@@ -23,16 +23,23 @@ export const GaussianDensityParams = {
 export const DefaultGaussianDensityProps = PD.getDefaultValues(GaussianDensityParams);
 export type GaussianDensityProps = typeof DefaultGaussianDensityProps
 
+//
+
 function getTextureMaxCells(webgl: WebGLContext) {
     const d = Math.min(webgl.maxTextureSize / 2, 4096);
     return d * d;
 }
 
+function largestUnitBox(structure: Structure) {
+    const units = structure.unitsSortedByVolume;
+    return units[units.length - 1].lookup3d.boundary.box;
+}
+
 //
 
 export function computeUnitGaussianDensity(structure: Structure, unit: Unit, props: GaussianDensityProps, webgl?: WebGLContext) {
     const { box } = unit.lookup3d.boundary;
-    const p = ensureReasonableResolution(box, props);
+    const p = ensureReasonableResolution(largestUnitBox(structure), props);
     const { position, radius } = getUnitConformationAndRadius(structure, unit, p);
     return Task.create('Gaussian Density', async ctx => {
         return await GaussianDensityCPU(ctx, position, box, radius, p);
@@ -41,7 +48,7 @@ export function computeUnitGaussianDensity(structure: Structure, unit: Unit, pro
 
 export function computeUnitGaussianDensityTexture(structure: Structure, unit: Unit, props: GaussianDensityProps, webgl: WebGLContext, texture?: Texture) {
     const { box } = unit.lookup3d.boundary;
-    const p = ensureReasonableResolution(box, props, getTextureMaxCells(webgl));
+    const p = ensureReasonableResolution(largestUnitBox(structure), props, getTextureMaxCells(webgl));
     const { position, radius } = getUnitConformationAndRadius(structure, unit, p);
     return Task.create('Gaussian Density', async ctx => {
         return GaussianDensityTexture(webgl, position, box, radius, p, texture);
@@ -50,7 +57,7 @@ export function computeUnitGaussianDensityTexture(structure: Structure, unit: Un
 
 export function computeUnitGaussianDensityTexture2d(structure: Structure, unit: Unit, powerOfTwo: boolean, props: GaussianDensityProps, webgl: WebGLContext, texture?: Texture) {
     const { box } = unit.lookup3d.boundary;
-    const p = ensureReasonableResolution(box, props, getTextureMaxCells(webgl));
+    const p = ensureReasonableResolution(largestUnitBox(structure), props, getTextureMaxCells(webgl));
     const { position, radius } = getUnitConformationAndRadius(structure, unit, p);
     return Task.create('Gaussian Density', async ctx => {
         return GaussianDensityTexture2d(webgl, position, box, radius, powerOfTwo, p, texture);