molstar
Version:
A comprehensive macromolecular library.
231 lines (230 loc) • 8.7 kB
JavaScript
/**
* Copyright (c) 2018-2025 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 { OrderedSet } from '../../../mol-data/int';
import { Sphere3D } from './sphere3d';
import { Vec3 } from '../../linear-algebra/3d/vec3';
function Box3D() {
return Box3D.zero();
}
(function (Box3D) {
function create(min, max) { return { min, max }; }
Box3D.create = create;
function zero() { return { min: Vec3(), max: Vec3() }; }
Box3D.zero = zero;
function copy(out, a) {
Vec3.copy(out.min, a.min);
Vec3.copy(out.max, a.max);
return out;
}
Box3D.copy = copy;
function set(out, min, max) {
Vec3.copy(out.min, min);
Vec3.copy(out.max, max);
return out;
}
Box3D.set = set;
function clone(a) {
return copy(zero(), a);
}
Box3D.clone = clone;
const tmpV = Vec3();
/** Get box from sphere, uses extrema if available */
function fromSphere3D(out, sphere) {
if (Sphere3D.hasExtrema(sphere) && sphere.extrema.length >= 14) { // 14 extrema with coarse boundary helper
return fromVec3Array(out, sphere.extrema);
}
Vec3.set(tmpV, sphere.radius, sphere.radius, sphere.radius);
Vec3.sub(out.min, sphere.center, tmpV);
Vec3.add(out.max, sphere.center, tmpV);
return out;
}
Box3D.fromSphere3D = fromSphere3D;
function addVec3Array(out, array) {
for (let i = 0, il = array.length; i < il; i++) {
add(out, array[i]);
}
return out;
}
Box3D.addVec3Array = addVec3Array;
function fromVec3Array(out, array) {
setEmpty(out);
addVec3Array(out, array);
return out;
}
Box3D.fromVec3Array = fromVec3Array;
function addSphere3D(out, sphere) {
if (Sphere3D.hasExtrema(sphere) && sphere.extrema.length >= 14) { // 14 extrema with coarse boundary helper
return addVec3Array(out, sphere.extrema);
}
add(out, Vec3.subScalar(tmpV, sphere.center, sphere.radius));
add(out, Vec3.addScalar(tmpV, sphere.center, sphere.radius));
return out;
}
Box3D.addSphere3D = addSphere3D;
function intersectsSphere3D(box, sphere) {
// Find the point on the AABB closest to the sphere center.
Vec3.clamp(tmpV, sphere.center, box.min, box.max);
// If that point is inside the sphere, the AABB and sphere intersect.
return Vec3.squaredDistance(tmpV, sphere.center) <= (sphere.radius * sphere.radius);
}
Box3D.intersectsSphere3D = intersectsSphere3D;
function computeBounding(data) {
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 { x, y, z, indices } = data;
for (let t = 0, _t = OrderedSet.size(indices); t < _t; t++) {
const i = OrderedSet.getAt(indices, t);
min[0] = Math.min(x[i], min[0]);
min[1] = Math.min(y[i], min[1]);
min[2] = Math.min(z[i], min[2]);
max[0] = Math.max(x[i], max[0]);
max[1] = Math.max(y[i], max[1]);
max[2] = Math.max(z[i], max[2]);
}
return { min, max };
}
Box3D.computeBounding = computeBounding;
/** Get size/extent of the box */
function size(size, box) {
return Vec3.sub(size, box.max, box.min);
}
Box3D.size = size;
const tmpSizeV = Vec3();
/** Get volume of the box */
function volume(box) {
size(tmpSizeV, box);
return tmpSizeV[0] * tmpSizeV[1] * tmpSizeV[2];
}
Box3D.volume = volume;
/** Sets min to Number.MAX_VALUE and max to -Number.MAX_VALUE */
function setEmpty(box) {
Vec3.set(box.min, Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
Vec3.set(box.max, -Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
return box;
}
Box3D.setEmpty = setEmpty;
/** Add point to box */
function add(box, point) {
Vec3.min(box.min, box.min, point);
Vec3.max(box.max, box.max, point);
return box;
}
Box3D.add = add;
/** Expand box by delta */
function expand(out, box, delta) {
Vec3.sub(out.min, box.min, delta);
Vec3.add(out.max, box.max, delta);
return out;
}
Box3D.expand = expand;
function expandUniformly(out, box, delta) {
Vec3.subScalar(out.min, box.min, delta);
Vec3.addScalar(out.max, box.max, delta);
return out;
}
Box3D.expandUniformly = expandUniformly;
function scale(out, box, scale) {
Vec3.scale(out.min, box.min, scale);
Vec3.scale(out.max, box.max, scale);
return out;
}
Box3D.scale = scale;
const tmpTransformV = Vec3();
/** Transform box with a Mat4 */
function transform(out, box, m) {
const [minX, minY, minZ] = box.min;
const [maxX, maxY, maxZ] = box.max;
setEmpty(out);
add(out, Vec3.transformMat4(tmpTransformV, Vec3.set(tmpTransformV, minX, minY, minZ), m));
add(out, Vec3.transformMat4(tmpTransformV, Vec3.set(tmpTransformV, minX, minY, maxZ), m));
add(out, Vec3.transformMat4(tmpTransformV, Vec3.set(tmpTransformV, minX, maxY, minZ), m));
add(out, Vec3.transformMat4(tmpTransformV, Vec3.set(tmpTransformV, minX, maxY, maxZ), m));
add(out, Vec3.transformMat4(tmpTransformV, Vec3.set(tmpTransformV, maxX, minY, minZ), m));
add(out, Vec3.transformMat4(tmpTransformV, Vec3.set(tmpTransformV, maxX, minY, maxZ), m));
add(out, Vec3.transformMat4(tmpTransformV, Vec3.set(tmpTransformV, maxX, maxY, minZ), m));
add(out, Vec3.transformMat4(tmpTransformV, Vec3.set(tmpTransformV, maxX, maxY, maxZ), m));
return out;
}
Box3D.transform = transform;
function containsVec3(box, v) {
return !(v[0] < box.min[0] || v[0] > box.max[0] ||
v[1] < box.min[1] || v[1] > box.max[1] ||
v[2] < box.min[2] || v[2] > box.max[2]);
}
Box3D.containsVec3 = containsVec3;
function overlaps(a, b) {
return !(a.max[0] < b.min[0] || a.min[0] > b.max[0] ||
a.max[1] < b.min[1] || a.min[1] > b.max[1] ||
a.max[2] < b.min[2] || a.min[2] > b.max[2]);
}
Box3D.overlaps = overlaps;
function containsSphere3D(box, s) {
const c = s.center;
const r = s.radius;
return (c[0] - r < box.min[0] || c[0] + r > box.max[0] ||
c[1] - r < box.min[1] || c[1] + r > box.max[1] ||
c[2] - r < box.min[2] || c[2] + r > box.max[2]) ? false : true;
}
Box3D.containsSphere3D = containsSphere3D;
function nearestIntersectionWithRay(out, box, origin, dir) {
const [minX, minY, minZ] = box.min;
const [maxX, maxY, maxZ] = box.max;
const [x, y, z] = origin;
const invDirX = 1.0 / dir[0];
const invDirY = 1.0 / dir[1];
const invDirZ = 1.0 / dir[2];
let tmin, tmax, tymin, tymax, tzmin, tzmax;
if (invDirX >= 0) {
tmin = (minX - x) * invDirX;
tmax = (maxX - x) * invDirX;
}
else {
tmin = (maxX - x) * invDirX;
tmax = (minX - x) * invDirX;
}
if (invDirY >= 0) {
tymin = (minY - y) * invDirY;
tymax = (maxY - y) * invDirY;
}
else {
tymin = (maxY - y) * invDirY;
tymax = (minY - y) * invDirY;
}
if (invDirZ >= 0) {
tzmin = (minZ - z) * invDirZ;
tzmax = (maxZ - z) * invDirZ;
}
else {
tzmin = (maxZ - z) * invDirZ;
tzmax = (minZ - z) * invDirZ;
}
if (tymin > tmin)
tmin = tymin;
if (tymax < tmax)
tmax = tymax;
if (tzmin > tmin)
tmin = tzmin;
if (tzmax < tmax)
tmax = tzmax;
Vec3.scale(out, dir, tmin);
return Vec3.set(out, out[0] + x, out[1] + y, out[2] + z);
}
Box3D.nearestIntersectionWithRay = nearestIntersectionWithRay;
function center(out, box) {
return Vec3.center(out, box.max, box.min);
}
Box3D.center = center;
function exactEquals(a, b) {
return Vec3.exactEquals(a.min, b.min) && Vec3.exactEquals(a.max, b.max);
}
Box3D.exactEquals = exactEquals;
function equals(a, b) {
return Vec3.equals(a.min, b.min) && Vec3.equals(a.max, b.max);
}
Box3D.equals = equals;
})(Box3D || (Box3D = {}));
export { Box3D };