ソースを参照

bounding sphere calculation for transformed boxes

- add Sphere.fromDimensionsAndTransform
- use for unitcell cage & volume
- precise image bounding sphere
Alexander Rose 4 年 前
コミット
70bde8c899

+ 3 - 14
src/mol-geo/geometry/direct-volume/direct-volume.ts

@@ -1,15 +1,14 @@
 /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
 import { hashFnv32a } from '../../../mol-data/util';
-import { transformPositionArray } from '../../../mol-geo/util';
 import { LocationIterator } from '../../../mol-geo/util/location-iterator';
 import { RenderableState } from '../../../mol-gl/renderable';
 import { DirectVolumeValues } from '../../../mol-gl/renderable/direct-volume';
-import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
+import { calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
 import { Texture } from '../../../mol-gl/webgl/texture';
 import { Box3D, Sphere3D } from '../../../mol-math/geometry';
 import { Mat4, Vec2, Vec3, Vec4 } from '../../../mol-math/linear-algebra';
@@ -226,16 +225,6 @@ export namespace DirectVolume {
 
 //
 
-const mTmp = Mat4.identity();
-const mTmp2 = Mat4.identity();
-const vHalfUnit = Vec3.create(0.5, 0.5, 0.5);
-const tmpVertices = new Float32Array(VolumeBox.vertices.length);
 function getBoundingSphere(gridDimension: Vec3, gridTransform: Mat4) {
-    tmpVertices.set(VolumeBox.vertices);
-    Mat4.fromTranslation(mTmp, vHalfUnit);
-    Mat4.mul(mTmp, Mat4.fromScaling(mTmp2, gridDimension), mTmp);
-    Mat4.mul(mTmp, gridTransform, mTmp);
-    transformPositionArray(mTmp, tmpVertices, 0, tmpVertices.length / 3);
-    return calculateInvariantBoundingSphere(tmpVertices, tmpVertices.length / 3, 1);
-    // return calculateBoundingSphere(tmpVertices, tmpVertices.length / 3, transform, transformCount)
+    return Sphere3D.fromDimensionsAndTransform(Sphere3D(), gridDimension, gridTransform);
 }

+ 21 - 3
src/mol-geo/geometry/image/image.ts

@@ -7,9 +7,9 @@
 import { hashFnv32a } from '../../../mol-data/util';
 import { LocationIterator } from '../../../mol-geo/util/location-iterator';
 import { RenderableState } from '../../../mol-gl/renderable';
-import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere, TextureImage } from '../../../mol-gl/renderable/util';
+import { calculateTransformBoundingSphere, TextureImage } from '../../../mol-gl/renderable/util';
 import { Sphere3D } from '../../../mol-math/geometry';
-import { Vec2, Vec4 } from '../../../mol-math/linear-algebra';
+import { Vec2, Vec4, Vec3 } from '../../../mol-math/linear-algebra';
 import { Theme } from '../../../mol-theme/theme';
 import { ValueCell } from '../../../mol-util';
 import { Color } from '../../../mol-util/color';
@@ -211,5 +211,23 @@ namespace Image {
 //
 
 function getBoundingSphere(corners: Float32Array) {
-    return calculateInvariantBoundingSphere(corners, corners.length / 3, 1);
+    const center = Vec3();
+    const extrema: Vec3[] = [];
+    for (let i = 0, il = corners.length; i < il; i += 3) {
+        const e = Vec3.fromArray(Vec3(), corners, i);
+        extrema.push(e);
+        Vec3.add(center, center, e);
+    }
+    Vec3.scale(center, center, 1 / (corners.length / 3));
+
+    let radius = 0;
+    for (const e of extrema) {
+        const d = Vec3.distance(center, e);
+        if (d > radius) radius = d;
+    }
+
+    const sphere = Sphere3D.create(center, radius);
+    Sphere3D.setExtrema(sphere, extrema);
+
+    return sphere;
 }

+ 40 - 0
src/mol-math/geometry/primitives/sphere3d.ts

@@ -92,6 +92,22 @@ namespace Sphere3D {
     export function transform(out: Sphere3D, sphere: Sphere3D, m: Mat4): Sphere3D {
         Vec3.transformMat4(out.center, sphere.center, m);
         out.radius = sphere.radius * Mat4.getMaxScaleOnAxis(m);
+        if (hasExtrema(sphere)) {
+            setExtrema(out, [
+                ...sphere.extrema.map(e => Vec3.transformMat4(Vec3(), e, m)),
+            ]);
+        }
+        return out;
+    }
+
+    /** Translate sphere by Vec3 */
+    export function translate(out: Sphere3D, sphere: Sphere3D, v: Vec3) {
+        Vec3.add(out.center, sphere.center, v);
+        if (hasExtrema(sphere)) {
+            setExtrema(out, [
+                ...sphere.extrema.map(e => Vec3.add(Vec3(), e, v)),
+            ]);
+        }
         return out;
     }
 
@@ -118,6 +134,30 @@ namespace Sphere3D {
         return out;
     }
 
+    const tmpCenter = Vec3();
+    /** Get a tight sphere around a transformed box */
+    export function fromDimensionsAndTransform(out: Sphere3D, dimensions: Vec3, transform: Mat4) {
+        const [x, y, z] = dimensions;
+
+        const cpA = Vec3.create(0, 0, 0); Vec3.transformMat4(cpA, cpA, transform);
+        const cpB = Vec3.create(x, y, z); Vec3.transformMat4(cpB, cpB, transform);
+        const cpC = Vec3.create(x, 0, 0); Vec3.transformMat4(cpC, cpC, transform);
+        const cpD = Vec3.create(0, y, z); Vec3.transformMat4(cpD, cpD, transform);
+
+        const cpE = Vec3.create(0, 0, z); Vec3.transformMat4(cpE, cpE, transform);
+        const cpF = Vec3.create(x, 0, z); Vec3.transformMat4(cpF, cpF, transform);
+        const cpG = Vec3.create(x, y, 0); Vec3.transformMat4(cpG, cpG, transform);
+        const cpH = Vec3.create(0, y, 0); Vec3.transformMat4(cpH, cpH, transform);
+
+        Vec3.add(tmpCenter, cpA, cpB);
+        Vec3.scale(tmpCenter, tmpCenter, 0.5);
+        const d = Math.max(Vec3.distance(cpA, cpB), Vec3.distance(cpC, cpD));
+        Sphere3D.set(out, tmpCenter, d / 2);
+        Sphere3D.setExtrema(out, [cpA, cpB, cpC, cpD, cpE, cpF, cpG, cpH]);
+
+        return out;
+    }
+
     const tmpAddVec3 = Vec3();
     export function addVec3(out: Sphere3D, s: Sphere3D, v: Vec3) {
         const d = Vec3.distance(s.center, v);

+ 9 - 1
src/mol-model/volume/grid.ts

@@ -5,7 +5,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
+import { SpacegroupCell, Box3D, Sphere3D } from '../../mol-math/geometry';
 import { Tensor, Mat4, Vec3 } from '../../mol-math/linear-algebra';
 
 /** The basic unit cell that contains the grid data. */
@@ -52,6 +52,14 @@ namespace Grid {
     export function isEmpty(grid: Grid) {
         return grid.cells.data.length === 0;
     }
+
+    export function getBoundingSphere(grid: Grid, boundingSphere?: Sphere3D) {
+        if (!boundingSphere) boundingSphere = Sphere3D();
+
+        const dimensions = grid.cells.space.dimensions as Vec3;
+        const transform = Grid.getGridToCartesianTransform(grid);
+        return Sphere3D.fromDimensionsAndTransform(boundingSphere, dimensions, transform);
+    }
 }
 
 export { Grid };

+ 1 - 23
src/mol-model/volume/volume.ts

@@ -100,29 +100,7 @@ export namespace Volume {
     export function isLociEmpty(loci: Loci) { return Grid.isEmpty(loci.volume.grid); }
 
     export function getBoundingSphere(volume: Volume, boundingSphere?: Sphere3D) {
-        if (!boundingSphere) boundingSphere = Sphere3D();
-
-        const transform = Grid.getGridToCartesianTransform(volume.grid);
-        const [x, y, z] = volume.grid.cells.space.dimensions;
-
-        const cpA = Vec3.create(0, 0, 0); Vec3.transformMat4(cpA, cpA, transform);
-        const cpB = Vec3.create(x, y, z); Vec3.transformMat4(cpB, cpB, transform);
-        const cpC = Vec3.create(x, 0, 0); Vec3.transformMat4(cpC, cpC, transform);
-        const cpD = Vec3.create(0, y, z); Vec3.transformMat4(cpD, cpC, transform);
-
-        const cpE = Vec3.create(0, 0, z); Vec3.transformMat4(cpE, cpE, transform);
-        const cpF = Vec3.create(x, 0, z); Vec3.transformMat4(cpF, cpF, transform);
-        const cpG = Vec3.create(x, y, 0); Vec3.transformMat4(cpG, cpG, transform);
-        const cpH = Vec3.create(0, y, 0); Vec3.transformMat4(cpH, cpH, transform);
-
-        const center = Vec3();
-        Vec3.add(center, cpA, cpB);
-        Vec3.scale(center, center, 0.5);
-        const d = Math.max(Vec3.distance(cpA, cpB), Vec3.distance(cpC, cpD));
-        Sphere3D.set(boundingSphere, center, d / 2);
-        Sphere3D.setExtrema(boundingSphere, [cpA, cpB, cpC, cpD, cpE, cpF, cpG, cpH]);
-
-        return boundingSphere;
+        return Grid.getBoundingSphere(volume.grid, boundingSphere);
     }
 
     export namespace Isosurface {

+ 3 - 24
src/mol-repr/shape/model/unitcell.ts

@@ -65,30 +65,9 @@ function getUnitcellMesh(data: UnitcellData, props: UnitcellProps, mesh?: Mesh)
     state.currentGroup = 1;
     MeshBuilder.addCage(state, fromFractional, cellCage, radius, 2, 20);
 
-    const cpA = Vec3.create(0, 0, 0);
-    Vec3.transformMat4(cpA, Vec3.add(cpA, cpA, tmpRef), fromFractional);
-    const cpB = Vec3.create(1, 1, 1);
-    Vec3.transformMat4(cpB, Vec3.add(cpB, cpB, tmpRef), fromFractional);
-    const cpC = Vec3.create(1, 0, 0);
-    Vec3.transformMat4(cpC, Vec3.add(cpC, cpC, tmpRef), fromFractional);
-    const cpD = Vec3.create(0, 1, 1);
-    Vec3.transformMat4(cpD, Vec3.add(cpD, cpD, tmpRef), fromFractional);
-
-    const cpE = Vec3.create(0, 0, 1);
-    Vec3.transformMat4(cpE, Vec3.add(cpE, cpE, tmpRef), fromFractional);
-    const cpF = Vec3.create(1, 0, 1);
-    Vec3.transformMat4(cpF, Vec3.add(cpF, cpF, tmpRef), fromFractional);
-    const cpG = Vec3.create(1, 1, 0);
-    Vec3.transformMat4(cpG, Vec3.add(cpG, cpG, tmpRef), fromFractional);
-    const cpH = Vec3.create(0, 1, 0);
-    Vec3.transformMat4(cpH, Vec3.add(cpH, cpH, tmpRef), fromFractional);
-
-    const center = Vec3();
-    Vec3.add(center, cpA, cpB);
-    Vec3.scale(center, center, 0.5);
-    const d = Math.max(Vec3.distance(cpA, cpB), Vec3.distance(cpC, cpD));
-    const sphere = Sphere3D.create(center, d / 2);
-    Sphere3D.setExtrema(sphere, [cpA, cpB, cpC, cpD, cpE, cpF, cpG, cpH]);
+    const sphere = Sphere3D.fromDimensionsAndTransform(Sphere3D(), Vec3.unit, fromFractional);
+    Vec3.transformMat4(tmpRef, tmpRef, fromFractional);
+    Sphere3D.translate(sphere, sphere, tmpRef);
     Sphere3D.expand(sphere, sphere, radius);
 
     const m = MeshBuilder.getMesh(state);

+ 6 - 2
src/mol-repr/volume/isosurface.ts

@@ -119,8 +119,10 @@ export async function createVolumeIsosurfaceMesh(ctx: VisualContext, volume: Vol
     }, mesh).runAsChild(ctx.runtime);
 
     const transform = Grid.getGridToCartesianTransform(volume.grid);
-    ctx.runtime.update({ message: 'Transforming mesh...' });
     Mesh.transform(surface, transform);
+
+    surface.setBoundingSphere(Volume.getBoundingSphere(volume));
+
     return surface;
 }
 
@@ -161,6 +163,8 @@ export async function createVolumeIsosurfaceWireframe(ctx: VisualContext, volume
     const transform = Grid.getGridToCartesianTransform(volume.grid);
     Lines.transform(wireframe, transform);
 
+    wireframe.setBoundingSphere(Volume.getBoundingSphere(volume));
+
     return wireframe;
 }
 
@@ -213,7 +217,7 @@ export function IsosurfaceRepresentation(ctx: RepresentationContext, getParams:
 export const IsosurfaceRepresentationProvider = VolumeRepresentationProvider({
     name: 'isosurface',
     label: 'Isosurface',
-    description: 'Displays an isosurface of volumetric data.',
+    description: 'Displays a triangulated isosurface of volumetric data.',
     factory: IsosurfaceRepresentation,
     getParams: getIsosurfaceParams,
     defaultValues: PD.getDefaultValues(IsosurfaceParams),