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 };