diff --git a/src/mol-gl/scene.ts b/src/mol-gl/scene.ts index a8278c282a6d3811e81e258aef3c82938eedc94c..c78f1883f5fdf991e36ea444ddedbedaa033c1a5 100644 --- a/src/mol-gl/scene.ts +++ b/src/mol-gl/scene.ts @@ -11,7 +11,9 @@ import { RenderObject, createRenderable } from './render-object'; import { Object3D } from './object3d'; import { Sphere3D } from 'mol-math/geometry'; import { Vec3 } from 'mol-math/linear-algebra'; +import { BoundaryHelper } from 'mol-math/geometry/boundary-helper'; +const boundaryHelper = new BoundaryHelper(); function calculateBoundingSphere(renderableMap: Map<RenderObject, Renderable<RenderableValues & BaseValues>>, boundingSphere: Sphere3D): Sphere3D { // let count = 0 // const center = Vec3.set(boundingSphere.center, 0, 0, 0) @@ -33,15 +35,20 @@ function calculateBoundingSphere(renderableMap: Map<RenderObject, Renderable<Ren // }) // boundingSphere.radius = radius - const spheres: Sphere3D[] = []; + boundaryHelper.reset(0.1); + + renderableMap.forEach(r => { + if (!r.state.visible || !r.boundingSphere.radius) return; + boundaryHelper.boundaryStep(r.boundingSphere.center, r.boundingSphere.radius); + }); + boundaryHelper.finishIncludeStep(); renderableMap.forEach(r => { if (!r.state.visible || !r.boundingSphere.radius) return; - spheres.push(r.boundingSphere) + boundaryHelper.extendStep(r.boundingSphere.center, r.boundingSphere.radius); }); - const bs = Sphere3D.getBoundingSphereFromSpheres(spheres, 0.1); - Vec3.copy(boundingSphere.center, bs.center); - boundingSphere.radius = bs.radius; + Vec3.copy(boundingSphere.center, boundaryHelper.center); + boundingSphere.radius = boundaryHelper.radius; return boundingSphere; } diff --git a/src/mol-math/geometry/boundary-helper.ts b/src/mol-math/geometry/boundary-helper.ts new file mode 100644 index 0000000000000000000000000000000000000000..d17943ba972bb0e73164707842726301e7052827 --- /dev/null +++ b/src/mol-math/geometry/boundary-helper.ts @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Vec3 } from 'mol-math/linear-algebra/3d'; +import { Box3D } from './primitives/box3d'; +import { Sphere3D } from './primitives/sphere3d'; + +export class BoundaryHelper { + private count = 0; + private extremes = [Vec3.zero(), Vec3.zero(), Vec3.zero(), Vec3.zero(), Vec3.zero(), Vec3.zero()]; + private u = Vec3.zero(); + private v = Vec3.zero(); + + tolerance = 0; + center: Vec3 = Vec3.zero(); + radius = 0; + + reset(tolerance: number) { + Vec3.set(this.center, 0, 0, 0); + for (let i = 0; i < 6; i++) { + const e = i % 2 === 0 ? Number.MAX_VALUE : -Number.MAX_VALUE; + this.extremes[i] = Vec3.create(e, e, e); + } + this.radius = 0; + this.count = 0; + this.tolerance = tolerance; + } + + boundaryStep(p: Vec3, r: number) { + updateExtremeMin(0, this.extremes[0], p, r); + updateExtremeMax(0, this.extremes[1], p, r); + + updateExtremeMin(1, this.extremes[2], p, r); + updateExtremeMax(1, this.extremes[3], p, r); + + updateExtremeMin(2, this.extremes[4], p, r); + updateExtremeMax(2, this.extremes[5], p, r); + this.count++; + } + + finishIncludeStep() { + if (this.count === 0) return; + + 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(this.extremes[i], this.extremes[j]); + if (d > maxSpan) { + maxSpan = d; + mI = i; + mJ = j; + } + } + } + + Vec3.add(this.center, this.extremes[mI], this.extremes[mJ]); + Vec3.scale(this.center, this.center, 0.5); + this.radius = Vec3.distance(this.center, this.extremes[mI]); + } + + extendStep(p: Vec3, r: number) { + const d = Vec3.distance(p, this.center); + if ((1 + this.tolerance) * this.radius >= r + d) return; + + Vec3.sub(this.u, p, this.center); + Vec3.normalize(this.u, this.u); + + Vec3.scale(this.v, this.u, -this.radius); + Vec3.add(this.v, this.v, this.center); + Vec3.scale(this.u, this.u, r + d); + Vec3.add(this.u, this.u, this.center); + + Vec3.add(this.center, this.u, this.v); + Vec3.scale(this.center, this.center, 0.5); + this.radius = 0.5 * (r + d + this.radius); + } + + getBox(): Box3D { + Vec3.copy(this.u, this.extremes[0]); + Vec3.copy(this.v, this.extremes[0]); + + for (let i = 1; i < 6; i++) { + Vec3.min(this.u, this.u, this.extremes[i]); + Vec3.max(this.v, this.v, this.extremes[i]); + } + + return { min: Vec3.clone(this.u), max: Vec3.clone(this.v) }; + } + + getSphere(): Sphere3D { + return { center: Vec3.clone(this.center), radius: this.radius }; + } + + constructor() { + this.reset(0); + } +} + +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; + } +} \ No newline at end of file diff --git a/src/mol-math/geometry/primitives/sphere3d.ts b/src/mol-math/geometry/primitives/sphere3d.ts index a6dcfc9a5a092a75938a07c6dfa4c7254e7b20e9..ef6c3c685583c12f131ca94ac1cf5419f61e57e3 100644 --- a/src/mol-math/geometry/primitives/sphere3d.ts +++ b/src/mol-math/geometry/primitives/sphere3d.ts @@ -96,83 +96,6 @@ 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