diff --git a/src/mol-gl/scene.ts b/src/mol-gl/scene.ts index 51815b14ead7f72119f143c0bb5bb2cf8a43f4c5..a8278c282a6d3811e81e258aef3c82938eedc94c 100644 --- a/src/mol-gl/scene.ts +++ b/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; } diff --git a/src/mol-math/geometry/primitives/sphere3d.ts b/src/mol-math/geometry/primitives/sphere3d.ts index ef6c3c685583c12f131ca94ac1cf5419f61e57e3..a6dcfc9a5a092a75938a07c6dfa4c7254e7b20e9 100644 --- a/src/mol-math/geometry/primitives/sphere3d.ts +++ b/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 } \ No newline at end of file