diff --git a/src/mol-gl/renderable/util.ts b/src/mol-gl/renderable/util.ts index 39dc8123448ac1a9f811312f4b52e5511a8e2eee..0bedceb0702eb55447020f31a0c75399d292e26e 100644 --- a/src/mol-gl/renderable/util.ts +++ b/src/mol-gl/renderable/util.ts @@ -6,7 +6,7 @@ import { Sphere3D } from '../../mol-math/geometry' import { Vec3 } from '../../mol-math/linear-algebra' -import { EposHelper, HierarchyHelper } from '../../mol-math/geometry/epos-helper'; +import { BoundaryHelper, HierarchyHelper } from '../../mol-math/geometry/boundary-helper'; export function calculateTextureInfo (n: number, itemSize: number) { const sqN = Math.sqrt(n) @@ -78,8 +78,8 @@ export function printImageData(imageData: ImageData, scale = 1, pixelated = fals // const v = Vec3.zero() -const eposHelperCoarse = new EposHelper('14') -const eposHelperFine = new EposHelper('98') +const eposHelperCoarse = new BoundaryHelper('14') +const eposHelperFine = new BoundaryHelper('98') const hierarchyHelperCoarse = new HierarchyHelper('14') const hierarchyHelperFine = new HierarchyHelper('98') diff --git a/src/mol-gl/scene.ts b/src/mol-gl/scene.ts index a60e6d04d41d8c95bcad8927e34aeaf6bbab994f..325e713bf6daea45ed5365895d7fbd8180157687 100644 --- a/src/mol-gl/scene.ts +++ b/src/mol-gl/scene.ts @@ -14,9 +14,9 @@ import { Sphere3D } from '../mol-math/geometry'; import { CommitQueue } from './commit-queue'; import { now } from '../mol-util/now'; import { arraySetRemove } from '../mol-util/array'; -import { EposHelper } from '../mol-math/geometry/epos-helper'; +import { BoundaryHelper } from '../mol-math/geometry/boundary-helper'; -const eposHelper = new EposHelper('98') +const eposHelper = new BoundaryHelper('98') function calculateBoundingSphere(renderables: Renderable<RenderableValues & BaseValues>[], boundingSphere: Sphere3D): Sphere3D { eposHelper.reset(); diff --git a/src/mol-math/geometry/boundary-helper.ts b/src/mol-math/geometry/boundary-helper.ts index ecde02d27af457c7c3452228f592fcb29da22c22..d9ecb8b7d5e3062f818c09b41edf564ea7011869 100644 --- a/src/mol-math/geometry/boundary-helper.ts +++ b/src/mol-math/geometry/boundary-helper.ts @@ -1,124 +1,213 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info. * - * @author David Sehnal <david.sehnal@gmail.com> + * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Vec3 } from '../../mol-math/linear-algebra/3d'; +import { Vec3 } from '../linear-algebra/3d'; +import { CentroidHelper } from './centroid-helper'; +import { Sphere3D } from '../geometry'; 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 - */ +// implementing http://www.ep.liu.se/ecp/034/009/ecp083409.pdf + 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; - Vec3.set(this.extremes[i], e, e, e); + private dir: Vec3[] + + private minDist: number[] = [] + private maxDist: number[] = [] + private extrema: Vec3[] = [] + centroidHelper = new CentroidHelper() + + private computeExtrema(i: number, p: Vec3) { + const d = Vec3.dot(this.dir[i], p) + + if (d < this.minDist[i]) { + this.minDist[i] = d + Vec3.copy(this.extrema[i * 2], p) + } + if (d > this.maxDist[i]) { + this.maxDist[i] = d + Vec3.copy(this.extrema[i * 2 + 1], p) + } + } + + private computeSphereExtrema(i: number, center: Vec3, radius: number) { + const d = Vec3.dot(this.dir[i], center) + + if (d - radius < this.minDist[i]) { + this.minDist[i] = d - radius + Vec3.scaleAndSub(this.extrema[i * 2], center, this.dir[i], radius) + } + if (d + radius > this.maxDist[i]) { + this.maxDist[i] = d + radius + Vec3.scaleAndAdd(this.extrema[i * 2 + 1], center, this.dir[i], radius) } - 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); + includeStep(p: Vec3) { + for (let i = 0, il = this.dir.length; i < il; ++i) { + this.computeExtrema(i, p) + } + } - updateExtremeMin(1, this.extremes[2], p, r); - updateExtremeMax(1, this.extremes[3], p, r); + includeSphereStep(center: Vec3, radius: number) { + for (let i = 0, il = this.dir.length; i < il; ++i) { + this.computeSphereExtrema(i, center, radius) + } + } + + finishedIncludeStep() { + for (let i = 0; i < this.extrema.length; i++) { + this.centroidHelper.includeStep(this.extrema[i]); + } + this.centroidHelper.finishedIncludeStep(); + } + + radiusStep(p: Vec3) { + this.centroidHelper.radiusStep(p); + } - updateExtremeMin(2, this.extremes[4], p, r); - updateExtremeMax(2, this.extremes[5], p, r); - this.count++; + radiusSphereStep(center: Vec3, radius: number) { + this.centroidHelper.radiusSphereStep(center, radius); } - finishBoundaryStep() { - if (this.count === 0) return; + getHierarchyInput() { + const sphere = this.centroidHelper.getSphere(); + const normal = Vec3() + const t = sphere.radius * this.hierarchyThresholdFactor - let maxSpan = 0, mI = 0, mJ = 0; + let maxDist = -Infinity + let belowThreshold = false - 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; - } + for (let i = 0; i < this.extrema.length; i += 2) { + const halfDist = Vec3.distance(this.extrema[i], this.extrema[i + 1]) / 2 + if (halfDist > maxDist) { + maxDist = halfDist + Vec3.normalize(normal, Vec3.sub(normal, this.extrema[i], this.extrema[i + 1])) } + if (halfDist < t) belowThreshold = true } - 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]); + return belowThreshold ? { sphere, normal } : false } - extendStep(p: Vec3, r: number) { - const d = Vec3.distance(p, this.center); - if ((1 + this.tolerance) * this.radius >= r + d) return; + getSphere(sphere?: Sphere3D) { + return this.centroidHelper.getSphere(sphere) + } - Vec3.sub(this.u, p, this.center); - Vec3.normalize(this.u, this.u); + getBox(box?: Box3D) { + // TODO can we get a tighter box from the extrema??? + if (!box) box = Box3D() + return Box3D.fromSphere3D(box, this.centroidHelper.getSphere()) + } - 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); + reset() { + for (let i = 0, il = this.dir.length; i < il; ++i) { + this.minDist[i] = Infinity + this.maxDist[i] = -Infinity + this.extrema[i * 2] = Vec3() + this.extrema[i * 2 + 1] = Vec3() + } + this.centroidHelper.reset() + } - Vec3.add(this.center, this.u, this.v); - Vec3.scale(this.center, this.center, 0.5); - this.radius = 0.5 * (r + d + this.radius); + constructor(quality: EposQuality, private hierarchyThresholdFactor = 0.66) { + this.dir = getEposDir(quality) + this.reset() } +} + +const tmpV = Vec3() - getBox(): Box3D { - Vec3.copy(this.u, this.extremes[0]); - Vec3.copy(this.v, this.extremes[0]); +export class HierarchyHelper { + private sphere = Sphere3D() + private normal = Vec3() + private helperA = new BoundaryHelper(this.quality) + private helperB = new BoundaryHelper(this.quality) - 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]); + private checkSide(p: Vec3) { + return Vec3.dot(this.normal, Vec3.sub(tmpV, this.sphere.center, p)) > 0 + } + + includeStep(p: Vec3) { + if (this.checkSide(p)) { + this.helperA.includeStep(p) + } else { + this.helperB.includeStep(p) } + } - return { min: Vec3.clone(this.u), max: Vec3.clone(this.v) }; + finishedIncludeStep() { + this.helperA.finishedIncludeStep(); + this.helperB.finishedIncludeStep(); } - getSphere(): Sphere3D { - return { center: Vec3.clone(this.center), radius: this.radius }; + radiusStep(p: Vec3) { + if (this.checkSide(p)) { + this.helperA.radiusStep(p) + } else { + this.helperB.radiusStep(p) + } } - constructor() { - this.reset(0); + getSphere(): Sphere3D.Hierarchy { + return { + center: this.sphere.center, + radius: this.sphere.radius, + hierarchy: [this.helperA.getSphere(), this.helperB.getSphere()] + } } -} -function updateExtremeMin(d: number, e: Vec3, center: Vec3, r: number) { - if (center[d] - r < e[d]) { - Vec3.copy(e, center); - e[d] -= r; + reset(sphere: Sphere3D, normal: Vec3) { + Sphere3D.copy(this.sphere, sphere) + Vec3.copy(this.normal, normal) + this.helperA.reset() + this.helperB.reset() } + + constructor(private quality: EposQuality) { } } -function updateExtremeMax(d: number, e: Vec3, center: Vec3, r: number) { - if (center[d] + r > e[d]) { - Vec3.copy(e, center); - e[d] += r; +type EposQuality = '6' | '14' | '26' | '98' + +function getEposDir(quality: EposQuality) { + let dir: number[][] + switch (quality) { + case '6': dir = [ ...Type001 ]; break + case '14': dir = [ ...Type001, ...Type111 ]; break + case '26': dir = [ ...Type001, ...Type111, ...Type011 ]; break + case '98': dir = [ ...Type001, ...Type111, ...Type011, ...Type012, ...Type112, ...Type122 ]; break } -} \ No newline at end of file + return dir.map(a => { + const v = Vec3.create(a[0], a[1], a[2]) + return Vec3.normalize(v, v) + }) +} + +const Type001 = [ + [1, 0, 0], [0, 1, 0], [0, 0, 1] +] + +const Type111 = [ + [1, 1, 1], [-1, 1, 1], [-1, -1, 1], [1, -1, 1] +] + +const Type011 = [ + [1, 1, 0], [1, -1, 0], [1, 0, 1], [1, 0, -1], [0, 1, 1], [0, 1, -1] +] + +const Type012 = [ + [0, 1, 2], [0, 2, 1], [1, 0, 2], [2, 0, 1], [1, 2, 0], [2, 1, 0], + [0, 1, -2], [0, 2, -1], [1, 0, -2], [2, 0, -1], [1, -2, 0], [2, -1, 0] +] + +const Type112 = [ + [1, 1, 2], [2, 1, 1], [1, 2, 1], [1, -1, 2], [1, 1, -2], [1, -1, -2], + [2, -1, 1], [2, 1, -1], [2, -1, -1], [1, -2, 1], [1, 2, -1], [1, -2, -1] +] + +const Type122 = [ + [2, 2, 1], [1, 2, 2], [2, 1, 2], [2, -2, 1], [2, 2, -1], [2, -2, -1], + [1, -2, 2], [1, 2, -2], [1, -2, -2], [2, -1, 2], [2, 1, -2], [2, -1, -2] +] \ No newline at end of file diff --git a/src/mol-math/geometry/epos-helper.ts b/src/mol-math/geometry/epos-helper.ts deleted file mode 100644 index 37e4c5dc6e79d072d9044dfe8376a9e784c1de61..0000000000000000000000000000000000000000 --- a/src/mol-math/geometry/epos-helper.ts +++ /dev/null @@ -1,206 +0,0 @@ -/** - * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author Alexander Rose <alexander.rose@weirdbyte.de> - */ - -import { Vec3 } from '../linear-algebra/3d'; -import { CentroidHelper } from './centroid-helper'; -import { Sphere3D } from '../geometry'; - -// implementing http://www.ep.liu.se/ecp/034/009/ecp083409.pdf - -export class EposHelper { - private dir: Vec3[] - - private minDist: number[] = [] - private maxDist: number[] = [] - private extrema: Vec3[] = [] - centroidHelper = new CentroidHelper() - - private computeExtrema(i: number, p: Vec3) { - const d = Vec3.dot(this.dir[i], p) - - if (d < this.minDist[i]) { - this.minDist[i] = d - Vec3.copy(this.extrema[i * 2], p) - } - if (d > this.maxDist[i]) { - this.maxDist[i] = d - Vec3.copy(this.extrema[i * 2 + 1], p) - } - } - - private computeSphereExtrema(i: number, center: Vec3, radius: number) { - const d = Vec3.dot(this.dir[i], center) - - if (d - radius < this.minDist[i]) { - this.minDist[i] = d - radius - Vec3.scaleAndSub(this.extrema[i * 2], center, this.dir[i], radius) - } - if (d + radius > this.maxDist[i]) { - this.maxDist[i] = d + radius - Vec3.scaleAndAdd(this.extrema[i * 2 + 1], center, this.dir[i], radius) - } - } - - includeStep(p: Vec3) { - for (let i = 0, il = this.dir.length; i < il; ++i) { - this.computeExtrema(i, p) - } - } - - includeSphereStep(center: Vec3, radius: number) { - for (let i = 0, il = this.dir.length; i < il; ++i) { - this.computeSphereExtrema(i, center, radius) - } - } - - finishedIncludeStep() { - for (let i = 0; i < this.extrema.length; i++) { - this.centroidHelper.includeStep(this.extrema[i]); - } - this.centroidHelper.finishedIncludeStep(); - } - - radiusStep(p: Vec3) { - this.centroidHelper.radiusStep(p); - } - - radiusSphereStep(center: Vec3, radius: number) { - this.centroidHelper.radiusSphereStep(center, radius); - } - - getHierarchyInput() { - const sphere = this.centroidHelper.getSphere(); - const normal = Vec3() - const t = sphere.radius * this.hierarchyThresholdFactor - - let maxDist = -Infinity - let belowThreshold = false - - for (let i = 0; i < this.extrema.length; i += 2) { - const halfDist = Vec3.distance(this.extrema[i], this.extrema[i + 1]) / 2 - if (halfDist > maxDist) { - maxDist = halfDist - Vec3.normalize(normal, Vec3.sub(normal, this.extrema[i], this.extrema[i + 1])) - } - if (halfDist < t) belowThreshold = true - } - - return belowThreshold ? { sphere, normal } : false - } - - getSphere(sphere?: Sphere3D) { - return this.centroidHelper.getSphere(sphere) - } - - reset() { - for (let i = 0, il = this.dir.length; i < il; ++i) { - this.minDist[i] = Infinity - this.maxDist[i] = -Infinity - this.extrema[i * 2] = Vec3() - this.extrema[i * 2 + 1] = Vec3() - } - this.centroidHelper.reset() - } - - constructor(quality: EposQuality, private hierarchyThresholdFactor = 0.66) { - this.dir = getEposDir(quality) - this.reset() - } -} - -const tmpV = Vec3() - -export class HierarchyHelper { - private sphere = Sphere3D() - private normal = Vec3() - private helperA = new EposHelper(this.quality) - private helperB = new EposHelper(this.quality) - - private checkSide(p: Vec3) { - return Vec3.dot(this.normal, Vec3.sub(tmpV, this.sphere.center, p)) > 0 - } - - includeStep(p: Vec3) { - if (this.checkSide(p)) { - this.helperA.includeStep(p) - } else { - this.helperB.includeStep(p) - } - } - - finishedIncludeStep() { - this.helperA.finishedIncludeStep(); - this.helperB.finishedIncludeStep(); - } - - radiusStep(p: Vec3) { - if (this.checkSide(p)) { - this.helperA.radiusStep(p) - } else { - this.helperB.radiusStep(p) - } - } - - getSphere(): Sphere3D.Hierarchy { - return { - center: this.sphere.center, - radius: this.sphere.radius, - hierarchy: [this.helperA.getSphere(), this.helperB.getSphere()] - } - } - - reset(sphere: Sphere3D, normal: Vec3) { - Sphere3D.copy(this.sphere, sphere) - Vec3.copy(this.normal, normal) - this.helperA.reset() - this.helperB.reset() - } - - constructor(private quality: EposQuality) { } -} - -type EposQuality = '6' | '14' | '26' | '98' - -function getEposDir(quality: EposQuality) { - let dir: number[][] - switch (quality) { - case '6': dir = [ ...Type001 ]; break - case '14': dir = [ ...Type001, ...Type111 ]; break - case '26': dir = [ ...Type001, ...Type111, ...Type011 ]; break - case '98': dir = [ ...Type001, ...Type111, ...Type011, ...Type012, ...Type112, ...Type122 ]; break - } - return dir.map(a => { - const v = Vec3.create(a[0], a[1], a[2]) - return Vec3.normalize(v, v) - }) -} - -const Type001 = [ - [1, 0, 0], [0, 1, 0], [0, 0, 1] -] - -const Type111 = [ - [1, 1, 1], [-1, 1, 1], [-1, -1, 1], [1, -1, 1] -] - -const Type011 = [ - [1, 1, 0], [1, -1, 0], [1, 0, 1], [1, 0, -1], [0, 1, 1], [0, 1, -1] -] - -const Type012 = [ - [0, 1, 2], [0, 2, 1], [1, 0, 2], [2, 0, 1], [1, 2, 0], [2, 1, 0], - [0, 1, -2], [0, 2, -1], [1, 0, -2], [2, 0, -1], [1, -2, 0], [2, -1, 0] -] - -const Type112 = [ - [1, 1, 2], [2, 1, 1], [1, 2, 1], [1, -1, 2], [1, 1, -2], [1, -1, -2], - [2, -1, 1], [2, 1, -1], [2, -1, -1], [1, -2, 1], [1, 2, -1], [1, -2, -1] -] - -const Type122 = [ - [2, 2, 1], [1, 2, 2], [2, 1, 2], [2, -2, 1], [2, 2, -1], [2, -2, -1], - [1, -2, 2], [1, 2, -2], [1, -2, -2], [2, -1, 2], [2, 1, -2], [2, -1, -2] -] \ No newline at end of file diff --git a/src/mol-math/geometry/lookup3d/grid.ts b/src/mol-math/geometry/lookup3d/grid.ts index be76a0696b90e3f5fd3c5a3079a437f0e459026f..a534fc2674f91e9dcbd1fd2f78290eaa30121f1d 100644 --- a/src/mol-math/geometry/lookup3d/grid.ts +++ b/src/mol-math/geometry/lookup3d/grid.ts @@ -166,21 +166,27 @@ function _build(state: BuildState): Grid3D { } } -const boundaryHelper = new BoundaryHelper(); +const boundaryHelperCoarse = new BoundaryHelper('14'); +const boundaryHelperFine = new BoundaryHelper('98'); +function getBoundaryHelper(count: number) { + return count > 500_000 ? boundaryHelperCoarse : boundaryHelperFine +} + function getBoundary(data: PositionData) { const { x, y, z, radius, indices } = data; const p = Vec3(); - boundaryHelper.reset(0); + const boundaryHelper = getBoundaryHelper(OrderedSet.size(indices)); + boundaryHelper.reset(); 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.includeSphereStep(p, (radius && radius[i]) || 0); } - boundaryHelper.finishBoundaryStep(); + boundaryHelper.finishedIncludeStep(); 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); + boundaryHelper.radiusSphereStep(p, (radius && radius[i]) || 0); } return { boundingBox: boundaryHelper.getBox(), boundingSphere: boundaryHelper.getSphere() }; diff --git a/src/mol-math/geometry/primitives/box3d.ts b/src/mol-math/geometry/primitives/box3d.ts index a6fd8ab521d7b3f5594fee78762ffe6a8ad39d19..7a7e16165c44310b2a4cf51b5dedb160ed5dc1c8 100644 --- a/src/mol-math/geometry/primitives/box3d.ts +++ b/src/mol-math/geometry/primitives/box3d.ts @@ -8,6 +8,7 @@ import { Vec3, Mat4 } from '../../linear-algebra' import { PositionData } from '../common' import { OrderedSet } from '../../../mol-data/int'; +import { Sphere3D } from './sphere3d'; interface Box3D { min: Vec3, max: Vec3 } @@ -29,6 +30,13 @@ namespace Box3D { return copy(empty(), a); } + export function fromSphere3D(out: Box3D, sphere: Sphere3D): Box3D { + const r = Vec3.create(sphere.radius, sphere.radius, sphere.radius) + Vec3.sub(out.min, sphere.center, r) + Vec3.add(out.max, sphere.center, r) + return out + } + export function computeBounding(data: PositionData): Box3D { 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); diff --git a/src/mol-math/geometry/primitives/sphere3d.ts b/src/mol-math/geometry/primitives/sphere3d.ts index 0fc5097d2c040c3406f91027adfea63c66c91f58..9fc9185f26bd8c273fbe2715facc0237f1fb6557 100644 --- a/src/mol-math/geometry/primitives/sphere3d.ts +++ b/src/mol-math/geometry/primitives/sphere3d.ts @@ -11,7 +11,6 @@ import { OrderedSet } from '../../../mol-data/int'; import { NumberArray } from '../../../mol-util/type-helpers'; import { Box3D } from './box3d'; import { Axes3D } from './axes3d'; -import { BoundaryHelper } from '../boundary-helper'; type Sphere3D = Sphere3D.Data | Sphere3D.Hierarchy @@ -111,15 +110,6 @@ namespace Sphere3D { return out } - const boundaryHelper = new BoundaryHelper(); - export function fromSphere3Ds(spheres: Sphere3D[]): Sphere3D { - boundaryHelper.reset(0); - for (const s of spheres) boundaryHelper.boundaryStep(s.center, s.radius); - boundaryHelper.finishBoundaryStep(); - for (const s of spheres) boundaryHelper.extendStep(s.center, s.radius); - return boundaryHelper.getSphere(); - } - const tmpAddVec3 = Vec3() export function addVec3(out: Sphere3D, s: Sphere3D, v: Vec3) { const d = Vec3.distance(s.center, v) diff --git a/src/mol-model/loci.ts b/src/mol-model/loci.ts index cfb793eb42d49d82d6729659ba49a55ac266a1af..d6eec5cab010a666ab6940768285cf1ca65426fa 100644 --- a/src/mol-model/loci.ts +++ b/src/mol-model/loci.ts @@ -14,6 +14,7 @@ import { PrincipalAxes } from '../mol-math/linear-algebra/matrix/principal-axes' import { ParamDefinition } from '../mol-util/param-definition'; import { shallowEqual } from '../mol-util'; import { FiniteArray } from '../mol-util/type-helpers'; +import { BoundaryHelper } from '../mol-math/geometry/boundary-helper'; /** A Loci that includes every loci */ export const EveryLoci = { kind: 'every-loci' as 'every-loci' } @@ -65,9 +66,14 @@ type Loci = StructureElement.Loci | Structure.Loci | Bond.Loci | EveryLoci | Emp namespace Loci { export interface Bundle<L extends number> { loci: FiniteArray<Loci, L> } + const boundaryHelper = new BoundaryHelper('98'); export function getBundleBoundingSphere(bundle: Bundle<any>): Sphere3D { - const spheres = bundle.loci.map(l => getBoundingSphere(l)).filter(s => !!s) - return Sphere3D.fromSphere3Ds(spheres as Sphere3D[]) + const spheres = bundle.loci.map(l => getBoundingSphere(l)).filter(s => !!s) as Sphere3D[] + boundaryHelper.reset(); + for (const s of spheres) boundaryHelper.includeSphereStep(s.center, s.radius); + boundaryHelper.finishedIncludeStep(); + for (const s of spheres) boundaryHelper.radiusSphereStep(s.center, s.radius); + return boundaryHelper.getSphere(); } export function areEqual(lociA: Loci, lociB: Loci) { diff --git a/src/mol-model/structure/structure/element/loci.ts b/src/mol-model/structure/structure/element/loci.ts index ffe5a13d5afa36d7c701cdfa54d8915fee2b555c..3e9c13faa2997eab7352a1b6b80e266d40638386 100644 --- a/src/mol-model/structure/structure/element/loci.ts +++ b/src/mol-model/structure/structure/element/loci.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> * @author Alexander Rose <alexander.rose@weirdbyte.de> @@ -7,7 +7,6 @@ import { UniqueArray } from '../../../../mol-data/generic'; import { OrderedSet, SortedArray, Interval } from '../../../../mol-data/int'; -import { BoundaryHelper } from '../../../../mol-math/geometry/boundary-helper'; import { Vec3 } from '../../../../mol-math/linear-algebra'; import { MolScriptBuilder as MS } from '../../../../mol-script/language/builder'; import Structure from '../structure'; @@ -22,6 +21,7 @@ import { ChainIndex } from '../../model/indexing'; import { PrincipalAxes } from '../../../../mol-math/linear-algebra/matrix/principal-axes'; import { NumberArray } from '../../../../mol-util/type-helpers'; import StructureProperties from '../properties'; +import { BoundaryHelper } from '../../../../mol-math/geometry/boundary-helper'; /** Represents multiple structure element index locations */ export interface Loci { @@ -456,10 +456,10 @@ export namespace Loci { // - const boundaryHelper = new BoundaryHelper(); + const boundaryHelper = new BoundaryHelper('98'); const tempPosBoundary = Vec3.zero(); export function getBoundary(loci: Loci): Boundary { - boundaryHelper.reset(0); + boundaryHelper.reset(); for (const e of loci.elements) { const { indices } = e; @@ -468,10 +468,10 @@ export namespace Loci { for (let i = 0, _i = OrderedSet.size(indices); i < _i; i++) { const eI = elements[OrderedSet.getAt(indices, i)]; pos(eI, tempPosBoundary); - boundaryHelper.boundaryStep(tempPosBoundary, r(eI)); + boundaryHelper.includeSphereStep(tempPosBoundary, r(eI)); } } - boundaryHelper.finishBoundaryStep(); + boundaryHelper.finishedIncludeStep(); for (const e of loci.elements) { const { indices } = e; const pos = e.unit.conformation.position, r = e.unit.conformation.r; @@ -479,7 +479,7 @@ export namespace Loci { for (let i = 0, _i = OrderedSet.size(indices); i < _i; i++) { const eI = elements[OrderedSet.getAt(indices, i)]; pos(eI, tempPosBoundary); - boundaryHelper.extendStep(tempPosBoundary, r(eI)); + boundaryHelper.radiusSphereStep(tempPosBoundary, r(eI)); } } diff --git a/src/mol-model/structure/structure/util/boundary.ts b/src/mol-model/structure/structure/util/boundary.ts index 978e1a5880b15d6cd99ba38b4d9f49bdeeefe44a..22e37c4f49bd7cc07e6ad45ae07a58fc9775ce13 100644 --- a/src/mol-model/structure/structure/util/boundary.ts +++ b/src/mol-model/structure/structure/util/boundary.ts @@ -1,74 +1,29 @@ /** - * 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 David Sehnal <david.sehnal@gmail.com> * @author Alexander Rose <alexander.rose@weirdbyte.de> */ import { Box3D, Sphere3D } from '../../../../mol-math/geometry'; -import { BoundaryHelper } from '../../../../mol-math/geometry/boundary-helper'; import { Vec3 } from '../../../../mol-math/linear-algebra'; import Structure from '../structure'; -import { CentroidHelper } from '../../../../mol-math/geometry/centroid-helper'; +import { BoundaryHelper } from '../../../../mol-math/geometry/boundary-helper'; export type Boundary = { box: Box3D, sphere: Sphere3D } -const tmpBox = Box3D.empty(); -const tmpSphere = Sphere3D.zero(); -const tmpVec = Vec3.zero(); +const tmpBox = Box3D(); +const tmpSphere = Sphere3D(); -const boundaryHelper = new BoundaryHelper(); -const centroidHelper = new CentroidHelper(); - -// TODO: show this be enabled? does not solve problem with "renderable bounding spheres" -const CENTROID_COMPUTATION_THRESHOLD = -1; +const boundaryHelper = new BoundaryHelper('98'); export function computeStructureBoundary(s: Structure): Boundary { - if (s.elementCount <= CENTROID_COMPUTATION_THRESHOLD && s.isAtomic) return computeStructureBoundaryFromElements(s); - return computeStructureBoundaryFromUnits(s); -} - -export function computeStructureBoundaryFromElements(s: Structure): Boundary { - centroidHelper.reset(); - - 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 v = tmpVec; - for (const unit of s.units) { - const { x, y, z } = unit.conformation; - const elements = unit.elements; - for (let j = 0, _j = elements.length; j < _j; j++) { - const e = elements[j]; - Vec3.set(v, x(e), y(e), z(e)); - centroidHelper.includeStep(v); - Vec3.min(min, min, v); - Vec3.max(max, max, v); - }; - } - - centroidHelper.finishedIncludeStep(); - - for (const unit of s.units) { - const { x, y, z } = unit.conformation; - const elements = unit.elements; - for (let j = 0, _j = elements.length; j < _j; j++) { - const e = elements[j]; - Vec3.set(v, x(e), y(e), z(e)); - centroidHelper.radiusStep(v); - }; - } - - return { box: { min, max }, sphere: centroidHelper.getSphere() }; -} - -export function computeStructureBoundaryFromUnits(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 { units } = s; - boundaryHelper.reset(0); + boundaryHelper.reset(); for (let i = 0, _i = units.length; i < _i; i++) { const u = units[i]; @@ -79,18 +34,18 @@ export function computeStructureBoundaryFromUnits(s: Structure): Boundary { Vec3.min(min, min, invariantBoundary.box.min); Vec3.max(max, max, invariantBoundary.box.max); - boundaryHelper.boundaryStep(invariantBoundary.sphere.center, invariantBoundary.sphere.radius); + boundaryHelper.includeSphereStep(invariantBoundary.sphere.center, invariantBoundary.sphere.radius); } else { 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); + boundaryHelper.includeSphereStep(tmpSphere.center, tmpSphere.radius); } } - boundaryHelper.finishBoundaryStep(); + boundaryHelper.finishedIncludeStep(); for (let i = 0, _i = units.length; i < _i; i++) { const u = units[i]; @@ -98,10 +53,10 @@ export function computeStructureBoundaryFromUnits(s: Structure): Boundary { const o = u.conformation.operator; if (o.isIdentity) { - boundaryHelper.extendStep(invariantBoundary.sphere.center, invariantBoundary.sphere.radius); + boundaryHelper.radiusSphereStep(invariantBoundary.sphere.center, invariantBoundary.sphere.radius); } else { Sphere3D.transform(tmpSphere, invariantBoundary.sphere, o.matrix); - boundaryHelper.extendStep(tmpSphere.center, tmpSphere.radius); + boundaryHelper.radiusSphereStep(tmpSphere.center, tmpSphere.radius); } } diff --git a/src/mol-plugin-state/manager/structure/selection.ts b/src/mol-plugin-state/manager/structure/selection.ts index 05133b039a1d6b732b1e7066b4476929ddfdc338..b4486b372790e64c6236baf832aa2923902106cc 100644 --- a/src/mol-plugin-state/manager/structure/selection.ts +++ b/src/mol-plugin-state/manager/structure/selection.ts @@ -13,13 +13,13 @@ import { Boundary } from '../../../mol-model/structure/structure/util/boundary'; import { PrincipalAxes } from '../../../mol-math/linear-algebra/matrix/principal-axes'; import { structureElementStatsLabel } from '../../../mol-theme/label'; import { OrderedSet } from '../../../mol-data/int'; -import { BoundaryHelper } from '../../../mol-math/geometry/boundary-helper'; import { arrayRemoveAtInPlace } from '../../../mol-util/array'; import { EmptyLoci, Loci } from '../../../mol-model/loci'; import { StateObject, StateSelection } from '../../../mol-state'; import { PluginStateObject } from '../../objects'; import { StructureSelectionQuery } from '../../helpers/structure-selection-query'; import { Task } from '../../../mol-task'; +import { BoundaryHelper } from '../../../mol-math/geometry/boundary-helper'; interface StructureSelectionManagerState { entries: Map<string, SelectionEntry>, @@ -27,12 +27,12 @@ interface StructureSelectionManagerState { stats?: SelectionStats } -const boundaryHelper = new BoundaryHelper(); +const boundaryHelper = new BoundaryHelper('98'); const HISTORY_CAPACITY = 8; export type StructureSelectionModifier = 'add' | 'remove' | 'set' -export class StructureSelectionManager extends PluginComponent<StructureSelectionManagerState> { +export class StructureSelectionManager extends PluginComponent<StructureSelectionManagerState> { readonly events = { changed: this.ev<undefined>() } @@ -83,7 +83,7 @@ export class StructureSelectionManager extends PluginComponent<StructureSelectio private add(loci: Loci): boolean { if (!StructureElement.Loci.is(loci)) return false; - + const entry = this.getEntry(loci.structure); if (!entry) return false; @@ -288,7 +288,7 @@ export class StructureSelectionManager extends PluginComponent<StructureSelectio 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) - boundaryHelper.reset(0); + boundaryHelper.reset(); const boundaries: Boundary[] = [] this.entries.forEach(v => { @@ -302,14 +302,12 @@ export class StructureSelectionManager extends PluginComponent<StructureSelectio const { box, sphere } = boundaries[i]; Vec3.min(min, min, box.min); Vec3.max(max, max, box.max); - boundaryHelper.boundaryStep(sphere.center, sphere.radius) + boundaryHelper.includeSphereStep(sphere.center, sphere.radius) } - - boundaryHelper.finishBoundaryStep(); - + boundaryHelper.finishedIncludeStep(); for (let i = 0, il = boundaries.length; i < il; ++i) { const { sphere } = boundaries[i]; - boundaryHelper.extendStep(sphere.center, sphere.radius); + boundaryHelper.radiusSphereStep(sphere.center, sphere.radius); } return { box: { min, max }, sphere: boundaryHelper.getSphere() }; diff --git a/src/mol-repr/structure/visual/label-text.ts b/src/mol-repr/structure/visual/label-text.ts index e72bc8a263998d3cde59454c6af7c077cba4ae51..6f2513709e66daddc8ab3da88b86d964706c9f9e 100644 --- a/src/mol-repr/structure/visual/label-text.ts +++ b/src/mol-repr/structure/visual/label-text.ts @@ -64,7 +64,7 @@ function createLabelText(ctx: VisualContext, structure: Structure, theme: Theme, // const tmpVec = Vec3(); -const boundaryHelper = new BoundaryHelper(); +const boundaryHelper = new BoundaryHelper('98'); function createChainText(ctx: VisualContext, structure: Structure, theme: Theme, props: LabelTextProps, text?: Text): Text { const l = StructureElement.Location.create(structure); @@ -117,20 +117,20 @@ function createResidueText(ctx: VisualContext, structure: Structure, theme: Them j++; while (j < jl && residueIndex[elements[j]] === rI) j++; - boundaryHelper.reset(0); + boundaryHelper.reset(); for (let eI = start; eI < j; eI++) { pos(elements[eI], tmpVec); - boundaryHelper.boundaryStep(tmpVec, 0); + boundaryHelper.includeStep(tmpVec); } - boundaryHelper.finishBoundaryStep(); + boundaryHelper.finishedIncludeStep(); for (let eI = start; eI < j; eI++) { pos(elements[eI], tmpVec); - boundaryHelper.extendStep(tmpVec, 0); + boundaryHelper.radiusStep(tmpVec); } l.element = elements[start]; - const { center, radius } = boundaryHelper + const { center, radius } = boundaryHelper.getSphere() const authSeqId = auth_seq_id(l) const compId = label_comp_id(l)