import { Box3 } from './Box3.js'; import { Vector3 } from './Vector3.js'; const _box = /*@__PURE__*/ new Box3(); const _v1 = /*@__PURE__*/ new Vector3(); const _toFarthestPoint = /*@__PURE__*/ new Vector3(); const _toPoint = /*@__PURE__*/ new Vector3(); class Sphere { constructor(center = new Vector3(), radius = -1) { this.center = center; this.radius = radius; } set(center, radius) { this.center.copy(center); this.radius = radius; return this; } setFromPoints(points, optionalCenter) { const center = this.center; if (optionalCenter !== undefined) { center.copy(optionalCenter); } else { _box.setFromPoints(points).getCenter(center); } let maxRadiusSq = 0; for (let i = 0, il = points.length; i < il; i++) { maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(points[i])); } this.radius = Math.sqrt(maxRadiusSq); return this; } copy(sphere) { this.center.copy(sphere.center); this.radius = sphere.radius; return this; } isEmpty() { return this.radius < 0; } makeEmpty() { this.center.set(0, 0, 0); this.radius = -1; return this; } containsPoint(point) { return point.distanceToSquared(this.center) <= this.radius * this.radius; } distanceToPoint(point) { return point.distanceTo(this.center) - this.radius; } intersectsSphere(sphere) { const radiusSum = this.radius + sphere.radius; return sphere.center.distanceToSquared(this.center) <= radiusSum * radiusSum; } intersectsBox(box) { return box.intersectsSphere(this); } intersectsPlane(plane) { return Math.abs(plane.distanceToPoint(this.center)) <= this.radius; } clampPoint(point, target) { const deltaLengthSq = this.center.distanceToSquared(point); target.copy(point); if (deltaLengthSq > this.radius * this.radius) { target.sub(this.center).normalize(); target.multiplyScalar(this.radius).add(this.center); } return target; } getBoundingBox(target) { if (this.isEmpty()) { // Empty sphere produces empty bounding box target.makeEmpty(); return target; } target.set(this.center, this.center); target.expandByScalar(this.radius); return target; } applyMatrix4(matrix) { this.center.applyMatrix4(matrix); this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; } translate(offset) { this.center.add(offset); return this; } expandByPoint(point) { // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L649-L671 _toPoint.subVectors(point, this.center); const lengthSq = _toPoint.lengthSq(); if (lengthSq > this.radius * this.radius) { const length = Math.sqrt(lengthSq); const missingRadiusHalf = (length - this.radius) * 0.5; // Nudge this sphere towards the target point. Add half the missing distance to radius, // and the other half to position. This gives a tighter enclosure, instead of if // the whole missing distance were just added to radius. this.center.add(_toPoint.multiplyScalar(missingRadiusHalf / length)); this.radius += missingRadiusHalf; } return this; } union(sphere) { // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L759-L769 // To enclose another sphere into this sphere, we only need to enclose two points: // 1) Enclose the farthest point on the other sphere into this sphere. // 2) Enclose the opposite point of the farthest point into this sphere. if (this.center.equals(sphere.center) === true) { _toFarthestPoint.set(0, 0, 1).multiplyScalar(sphere.radius); } else { _toFarthestPoint.subVectors(sphere.center, this.center).normalize().multiplyScalar(sphere.radius); } this.expandByPoint(_v1.copy(sphere.center).add(_toFarthestPoint)); this.expandByPoint(_v1.copy(sphere.center).sub(_toFarthestPoint)); return this; } equals(sphere) { return sphere.center.equals(this.center) && sphere.radius === this.radius; } clone() { return new this.constructor().copy(this); } } export { Sphere };