Bladeren bron

improved scene bounding sphere computation

David Sehnal 6 jaren geleden
bovenliggende
commit
adf0dc2e55
2 gewijzigde bestanden met toevoegingen van 104 en 17 verwijderingen
  1. 27 17
      src/mol-gl/scene.ts
  2. 77 0
      src/mol-math/geometry/primitives/sphere3d.ts

+ 27 - 17
src/mol-gl/scene.ts

@@ -13,25 +13,35 @@ import { Sphere3D } from 'mol-math/geometry';
 import { Vec3 } from 'mol-math/linear-algebra';
 
 function calculateBoundingSphere(renderableMap: Map<RenderObject, Renderable<RenderableValues & BaseValues>>, boundingSphere: Sphere3D): Sphere3D {
-    let count = 0
-    const center = Vec3.set(boundingSphere.center, 0, 0, 0)
-    renderableMap.forEach(r => {
-        if (r.boundingSphere.radius) {
-            Vec3.add(center, center, r.boundingSphere.center)
-            ++count
-        }
-    })
-    if (count > 0) {
-        Vec3.scale(center, center, 1 / count)
-    }
+    // let count = 0
+    // const center = Vec3.set(boundingSphere.center, 0, 0, 0)
+    // renderableMap.forEach(r => {
+    //     if (r.boundingSphere.radius) {
+    //         Vec3.add(center, center, r.boundingSphere.center)
+    //         ++count
+    //     }
+    // })
+    // if (count > 0) {
+    //     Vec3.scale(center, center, 1 / count)
+    // }
 
-    let radius = 0
+    // let radius = 0
+    // renderableMap.forEach(r => {
+    //     if (r.boundingSphere.radius) {
+    //         radius = Math.max(radius, Vec3.distance(center, r.boundingSphere.center) + r.boundingSphere.radius)
+    //     }
+    // })
+    // boundingSphere.radius = radius
+
+    const spheres: Sphere3D[] = [];
     renderableMap.forEach(r => {
-        if (r.boundingSphere.radius) {
-            radius = Math.max(radius, Vec3.distance(center, r.boundingSphere.center) + r.boundingSphere.radius)
-        }
-    })
-    boundingSphere.radius = radius
+        if (!r.state.visible || !r.boundingSphere.radius) return;
+        spheres.push(r.boundingSphere)
+    });
+    const bs = Sphere3D.getBoundingSphereFromSpheres(spheres, 0.1);
+
+    Vec3.copy(boundingSphere.center, bs.center);
+    boundingSphere.radius = bs.radius;
 
     return boundingSphere;
 }

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

@@ -96,6 +96,83 @@ namespace Sphere3D {
         return (Math.abs(ar - br) <= EPSILON.Value * Math.max(1.0, Math.abs(ar), Math.abs(br)) &&
                 Vec3.equals(a.center, b.center));
     }
+
+    function updateExtremeMin(d: number, e: Vec3, center: Vec3, r: number) {
+        if (center[d] - r < e[d]) {
+            Vec3.copy(e, center);
+            e[d] -= r;
+        }
+    }
+
+    function updateExtremeMax(d: number, e: Vec3, center: Vec3, r: number) {
+        if (center[d] + r > e[d]) {
+            Vec3.copy(e, center);
+            e[d] += r;
+        }
+    }
+
+    export function getBoundingSphereFromSpheres(spheres: Sphere3D[], tolerance: number): Sphere3D {
+        if (spheres.length === 0) {
+            return { center: Vec3.zero(), radius: 0.1 };
+        }
+
+        const extremes: Vec3[] = [];
+        for (let i = 0; i < 6; i++) {
+            const e = i % 2 === 0 ? Number.MAX_VALUE : -Number.MAX_VALUE;
+            extremes[i] = Vec3.create(e, e, e);
+        }
+        const u = Vec3.zero(), v = Vec3.zero();
+
+        let m = 0;
+        for (const s of spheres) {
+            updateExtremeMin(0, extremes[0], s.center, s.radius);
+            updateExtremeMax(0, extremes[1], s.center, s.radius);
+
+            updateExtremeMin(1, extremes[2], s.center, s.radius);
+            updateExtremeMax(1, extremes[3], s.center, s.radius);
+
+            updateExtremeMin(2, extremes[4], s.center, s.radius);
+            updateExtremeMax(2, extremes[5], s.center, s.radius);
+            if (s.radius > m) m = s.radius;
+        }
+
+        let maxSpan = 0, mI = 0, mJ = 0;
+
+        for (let i = 0; i < 5; i++) {
+            for (let j = i + 1; j < 6; j++) {
+                const d = Vec3.squaredDistance(extremes[i], extremes[j]);
+                if (d > maxSpan) {
+                    maxSpan = d;
+                    mI = i;
+                    mJ = j;
+                }
+            }
+        }
+
+        const center = Vec3.zero();
+        Vec3.add(center, extremes[mI], extremes[mJ]);
+        Vec3.scale(center, center, 0.5);
+        let radius = Vec3.distance(center, extremes[mI]);
+
+        for (const s of spheres) {
+            const d = Vec3.distance(s.center, center);
+            if ((1 + tolerance) * radius >= s.radius + d) continue;
+
+            Vec3.sub(u, s.center, center);
+            Vec3.normalize(u, u);
+
+            Vec3.scale(v, u, -radius);
+            Vec3.add(v, v, center);
+            Vec3.scale(u, u, s.radius + d);
+            Vec3.add(u, u, center);
+
+            Vec3.add(center, u, v);
+            Vec3.scale(center, center, 0.5);
+            radius = Vec3.distance(center, u);
+        }
+
+        return { center, radius };
+    }
 }
 
 export { Sphere3D }