diff --git a/src/mol-gl/scene.ts b/src/mol-gl/scene.ts index c78f1883f5fdf991e36ea444ddedbedaa033c1a5..02f2cee8a516a33ef66a2193db4e45518f90c3f6 100644 --- a/src/mol-gl/scene.ts +++ b/src/mol-gl/scene.ts @@ -41,7 +41,7 @@ function calculateBoundingSphere(renderableMap: Map<RenderObject, Renderable<Ren if (!r.state.visible || !r.boundingSphere.radius) return; boundaryHelper.boundaryStep(r.boundingSphere.center, r.boundingSphere.radius); }); - boundaryHelper.finishIncludeStep(); + boundaryHelper.finishBoundaryStep(); renderableMap.forEach(r => { if (!r.state.visible || !r.boundingSphere.radius) return; boundaryHelper.extendStep(r.boundingSphere.center, r.boundingSphere.radius); diff --git a/src/mol-math/geometry/boundary-helper.ts b/src/mol-math/geometry/boundary-helper.ts index d17943ba972bb0e73164707842726301e7052827..76ccb3aa69e2cc44a2ebac0cf9375fe49f785aa7 100644 --- a/src/mol-math/geometry/boundary-helper.ts +++ b/src/mol-math/geometry/boundary-helper.ts @@ -8,6 +8,15 @@ import { Vec3 } from 'mol-math/linear-algebra/3d'; import { Box3D } from './primitives/box3d'; import { Sphere3D } from './primitives/sphere3d'; +/** + * Usage: + * + * 1. .reset(tolerance); tolerance plays part in the "extend" step + * 2. for each point/sphere call boundaryStep() + * 3. .finishBoundaryStep + * 4. for each point/sphere call extendStep + * 5. use .center/.radius or call getSphere/getBox + */ export class BoundaryHelper { private count = 0; private extremes = [Vec3.zero(), Vec3.zero(), Vec3.zero(), Vec3.zero(), Vec3.zero(), Vec3.zero()]; @@ -41,7 +50,7 @@ export class BoundaryHelper { this.count++; } - finishIncludeStep() { + finishBoundaryStep() { if (this.count === 0) return; let maxSpan = 0, mI = 0, mJ = 0; diff --git a/src/mol-math/geometry/lookup3d/grid.ts b/src/mol-math/geometry/lookup3d/grid.ts index 6fa09806898f2d7122b88a673613ca356d17a4a6..aa9393220b348ac1b0a206fc464bf95d38ca21b5 100644 --- a/src/mol-math/geometry/lookup3d/grid.ts +++ b/src/mol-math/geometry/lookup3d/grid.ts @@ -11,6 +11,7 @@ import { Sphere3D } from '../primitives/sphere3d'; import { PositionData } from '../common'; import { Vec3 } from '../../linear-algebra'; import { OrderedSet } from 'mol-data/int'; +import { BoundaryHelper } from '../boundary-helper'; interface GridLookup3D<T = number> extends Lookup3D<T> { readonly buckets: { readonly offset: ArrayLike<number>, readonly count: ArrayLike<number>, readonly array: ArrayLike<number> } @@ -163,11 +164,30 @@ function _build(state: BuildState): Grid3D { } } +const boundaryHelper = new BoundaryHelper(); +function getBoundary(data: PositionData) { + const { x, y, z, radius, indices } = data; + const p = Vec3.zero(); + boundaryHelper.reset(0); + for (let t = 0, _t = OrderedSet.size(indices); t < _t; t++) { + const i = OrderedSet.getAt(indices, t); + Vec3.set(p, x[i], y[i], z[i]); + boundaryHelper.boundaryStep(p, (radius && radius[i]) || 0); + } + boundaryHelper.finishBoundaryStep(); + for (let t = 0, _t = OrderedSet.size(indices); t < _t; t++) { + const i = OrderedSet.getAt(indices, t); + Vec3.set(p, x[i], y[i], z[i]); + boundaryHelper.extendStep(p, (radius && radius[i]) || 0); + } + + return { boundingBox: boundaryHelper.getBox(), boundingSphere: boundaryHelper.getSphere() }; +} + function build(data: PositionData, cellSize?: Vec3) { - const boundingBox = Box3D.computeBounding(data); + const { boundingBox, boundingSphere } = getBoundary(data); // need to expand the grid bounds to avoid rounding errors const expandedBox = Box3D.expand(Box3D.empty(), boundingBox, Vec3.create(0.5, 0.5, 0.5)); - const boundingSphere = Sphere3D.computeBounding(data); const { indices } = data; const S = Vec3.sub(Vec3.zero(), expandedBox.max, expandedBox.min); diff --git a/src/mol-model/structure/structure/util/boundary.ts b/src/mol-model/structure/structure/util/boundary.ts index b90370614883b4518fb361814074a6696bb74090..e283f325bae318b809fd24f4b614e6011aa7aac5 100644 --- a/src/mol-model/structure/structure/util/boundary.ts +++ b/src/mol-model/structure/structure/util/boundary.ts @@ -5,102 +5,60 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import Structure from '../structure' -import Unit from '../unit'; -import { Box3D, Sphere3D, SymmetryOperator } from 'mol-math/geometry'; +import { Box3D, Sphere3D } from 'mol-math/geometry'; +import { BoundaryHelper } from 'mol-math/geometry/boundary-helper'; import { Vec3 } from 'mol-math/linear-algebra'; -import { SortedArray } from 'mol-data/int'; -import { ElementIndex } from '../../model/indexing'; +import Structure from '../structure'; export type Boundary = { box: Box3D, sphere: Sphere3D } -function computeElementsPositionBoundary(elements: SortedArray<ElementIndex>, position: SymmetryOperator.CoordinateMapper): Boundary { - const min = Vec3.create(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE) - const max = Vec3.create(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE) - const center = Vec3.zero() - - let radiusSq = 0 - let size = 0 - - const p = Vec3.zero() - - size += elements.length - for (let j = 0, _j = elements.length; j < _j; j++) { - position(elements[j], p) - Vec3.min(min, min, p) - Vec3.max(max, max, p) - Vec3.add(center, center, p) - } - - if (size > 0) Vec3.scale(center, center, 1/size) - - for (let j = 0, _j = elements.length; j < _j; j++) { - position(elements[j], p) - const d = Vec3.squaredDistance(p, center) - if (d > radiusSq) radiusSq = d - } - - return { - box: { min, max }, - sphere: { center, radius: Math.sqrt(radiusSq) } - } -} - -function computeInvariantUnitBoundary(u: Unit): Boundary { - return computeElementsPositionBoundary(u.elements, u.conformation.invariantPosition) -} - -export function computeUnitBoundary(u: Unit): Boundary { - return computeElementsPositionBoundary(u.elements, u.conformation.position) -} - const tmpBox = Box3D.empty() const tmpSphere = Sphere3D.zero() +const boundaryHelper = new BoundaryHelper(); + export function computeStructureBoundary(s: Structure): Boundary { const min = Vec3.create(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE) const max = Vec3.create(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE) - const center = Vec3.zero() - const { units } = s + const { units } = s; + + boundaryHelper.reset(0); + + for (let i = 0, _i = units.length; i < _i; i++) { + const u = units[i]; + const invariantBoundary = u.lookup3d.boundary; + const o = u.conformation.operator; + + if (o.isIdentity) { + Vec3.min(min, min, invariantBoundary.box.min); + Vec3.max(max, max, invariantBoundary.box.max); - const boundaryMap: Map<number, Boundary> = new Map() - function getInvariantBoundary(u: Unit) { - let boundary: Boundary - if (boundaryMap.has(u.invariantId)) { - boundary = boundaryMap.get(u.invariantId)! + boundaryHelper.boundaryStep(invariantBoundary.sphere.center, invariantBoundary.sphere.radius); } else { - boundary = computeInvariantUnitBoundary(u) - boundaryMap.set(u.invariantId, boundary) + Box3D.transform(tmpBox, invariantBoundary.box, o.matrix); + Vec3.min(min, min, tmpBox.min); + Vec3.max(max, max, tmpBox.max); + + Sphere3D.transform(tmpSphere, invariantBoundary.sphere, o.matrix); + boundaryHelper.boundaryStep(tmpSphere.center, tmpSphere.radius); } - return boundary } - let radius = 0 - let size = 0 + boundaryHelper.finishBoundaryStep(); for (let i = 0, _i = units.length; i < _i; i++) { - const u = units[i] - const invariantBoundary = getInvariantBoundary(u) - const m = u.conformation.operator.matrix - size += u.elements.length - Box3D.transform(tmpBox, invariantBoundary.box, m) - Vec3.min(min, min, tmpBox.min) - Vec3.max(max, max, tmpBox.max) - Sphere3D.transform(tmpSphere, invariantBoundary.sphere, m) - Vec3.scaleAndAdd(center, center, tmpSphere.center, u.elements.length) - } - - if (size > 0) Vec3.scale(center, center, 1/size) + const u = units[i]; + const invariantBoundary = u.lookup3d.boundary; + const o = u.conformation.operator; - for (let i = 0, _i = units.length; i < _i; i++) { - const u = units[i] - const invariantBoundary = getInvariantBoundary(u) - const m = u.conformation.operator.matrix - Sphere3D.transform(tmpSphere, invariantBoundary.sphere, m) - const d = Vec3.distance(tmpSphere.center, center) + tmpSphere.radius - if (d > radius) radius = d + if (o.isIdentity) { + boundaryHelper.extendStep(invariantBoundary.sphere.center, invariantBoundary.sphere.radius); + } else { + Sphere3D.transform(tmpSphere, invariantBoundary.sphere, o.matrix); + boundaryHelper.extendStep(tmpSphere.center, tmpSphere.radius); + } } - return { box: { min, max }, sphere: { center, radius } } + return { box: { min, max }, sphere: boundaryHelper.getSphere() }; } \ No newline at end of file