text: import { Vector3 } from './Vector3.js';
  
  const _vector = /*@__PURE__*/ new Vector3();
  const _segCenter = /*@__PURE__*/ new Vector3();
  const _segDir = /*@__PURE__*/ new Vector3();
  const _diff = /*@__PURE__*/ new Vector3();
  
  const _edge1 = /*@__PURE__*/ new Vector3();
  const _edge2 = /*@__PURE__*/ new Vector3();
  const _normal = /*@__PURE__*/ new Vector3();
  
  class Ray {
  
  	constructor( origin = new Vector3(), direction = new Vector3( 0, 0, - 1 ) ) {
  
  		this.origin = origin;
  		this.direction = direction;
  
  	}
  
  	set( origin, direction ) {
  
  		this.origin.copy( origin );
  		this.direction.copy( direction );
  
  		return this;
  
  	}
  
  	copy( ray ) {
  
  		this.origin.copy( ray.origin );
  		this.direction.copy( ray.direction );
  
  		return this;
  
  	}
  
  	at( t, target ) {
  
  		return target.copy( this.direction ).multiplyScalar( t ).add( this.origin );
  
  	}
  
  	lookAt( v ) {
  
  		this.direction.copy( v ).sub( this.origin ).normalize();
  
  		return this;
  
  	}
  
  	recast( t ) {
  
  		this.origin.copy( this.at( t, _vector ) );
  
  		return this;
  
  	}
  
  	closestPointToPoint( point, target ) {
  
  		target.subVectors( point, this.origin );
  
  		const directionDistance = target.dot( this.direction );
  
  		if ( directionDistance < 0 ) {
  
  			return target.copy( this.origin );
  
  		}
  
  		return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
  
  	}
  
  	distanceToPoint( point ) {
  
  		return Math.sqrt( this.distanceSqToPoint( point ) );
  
  	}
  
  	distanceSqToPoint( point ) {
  
  		const directionDistance = _vector.subVectors( point, this.origin ).dot( this.direction );
  
  		// point behind the ray
  
  		if ( directionDistance < 0 ) {
  
  			return this.origin.distanceToSquared( point );
  
  		}
  
  		_vector.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
  
  		return _vector.distanceToSquared( point );
  
  	}
  
  	distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {
  
  		// from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h
  		// It returns the min distance between the ray and the segment
  		// defined by v0 and v1
  		// It can also set two optional targets :
  		// - The closest point on the ray
  		// - The closest point on the segment
  
  		_segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 );
  		_segDir.copy( v1 ).sub( v0 ).normalize();
  		_diff.copy( this.origin ).sub( _segCenter );
  
  		const segExtent = v0.distanceTo( v1 ) * 0.5;
  		const a01 = - this.direction.dot( _segDir );
  		const b0 = _diff.dot( this.direction );
  		const b1 = - _diff.dot( _segDir );
  		const c = _diff.lengthSq();
  		const det = Math.abs( 1 - a01 * a01 );
  		let s0, s1, sqrDist, extDet;
  
  		if ( det > 0 ) {
  
  			// The ray and segment are not parallel.
  
  			s0 = a01 * b1 - b0;
  			s1 = a01 * b0 - b1;
  			extDet = segExtent * det;
  
  			if ( s0 >= 0 ) {
  
  				if ( s1 >= - extDet ) {
  
  					if ( s1 <= extDet ) {
  
  						// region 0
  						// Minimum at interior points of ray and segment.
  
  						const invDet = 1 / det;
  						s0 *= invDet;
  						s1 *= invDet;
  						sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;
  
  					} else {
  
  						// region 1
  
  						s1 = segExtent;
  						s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
  						sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  
  					}
  
  				} else {
  
  					// region 5
  
  					s1 = - segExtent;
  					s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
  					sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  
  				}
  
  			} else {
  
  				if ( s1 <= - extDet ) {
  
  					// region 4
  
  					s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );
  					s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
  					sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  
  				} else if ( s1 <= extDet ) {
  
  					// region 3
  
  					s0 = 0;
  					s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );
  					sqrDist = s1 * ( s1 + 2 * b1 ) + c;
  
  				} else {
  
  					// region 2
  
  					s0 = Math.max( 0, - ( a01 * segExtent + b0 ) );
  					s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
  					sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  
  				}
  
  			}
  
  		} else {
  
  			// Ray and segment are parallel.
  
  			s1 = ( a01 > 0 ) ? - segExtent : segExtent;
  			s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
  			sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  
  		}
  
  		if ( optionalPointOnRay ) {
  
  			optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin );
  
  		}
  
  		if ( optionalPointOnSegment ) {
  
  			optionalPointOnSegment.copy( _segDir ).multiplyScalar( s1 ).add( _segCenter );
  
  		}
  
  		return sqrDist;
  
  	}
  
  	intersectSphere( sphere, target ) {
  
  		_vector.subVectors( sphere.center, this.origin );
  		const tca = _vector.dot( this.direction );
  		const d2 = _vector.dot( _vector ) - tca * tca;
  		const radius2 = sphere.radius * sphere.radius;
  
  		if ( d2 > radius2 ) return null;
  
  		const thc = Math.sqrt( radius2 - d2 );
  
  		// t0 = first intersect point - entrance on front of sphere
  		const t0 = tca - thc;
  
  		// t1 = second intersect point - exit point on back of sphere
  		const t1 = tca + thc;
  
  		// test to see if both t0 and t1 are behind the ray - if so, return null
  		if ( t0 < 0 && t1 < 0 ) return null;
  
  		// test to see if t0 is behind the ray:
  		// if it is, the ray is inside the sphere, so return the second exit point scaled by t1,
  		// in order to always return an intersect point that is in front of the ray.
  		if ( t0 < 0 ) return this.at( t1, target );
  
  		// else t0 is in front of the ray, so return the first collision point scaled by t0
  		return this.at( t0, target );
  
  	}
  
  	intersectsSphere( sphere ) {
  
  		return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius );
  
  	}
  
  	distanceToPlane( plane ) {
  
  		const denominator = plane.normal.dot( this.direction );
  
  		if ( denominator === 0 ) {
  
  			// line is coplanar, return origin
  			if ( plane.distanceToPoint( this.origin ) === 0 ) {
  
  				return 0;
  
  			}
  
  			// Null is preferable to undefined since undefined means.... it is undefined
  
  			return null;
  
  		}
  
  		const t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;
  
  		// Return if the ray never intersects the plane
  
  		return t >= 0 ? t : null;
  
  	}
  
  	intersectPlane( plane, target ) {
  
  		const t = this.distanceToPlane( plane );
  
  		if ( t === null ) {
  
  			return null;
  
  		}
  
  		return this.at( t, target );
  
  	}
  
  	intersectsPlane( plane ) {
  
  		// check if the ray lies on the plane first
  
  		const distToPoint = plane.distanceToPoint( this.origin );
  
  		if ( distToPoint === 0 ) {
  
  			return true;
  
  		}
  
  		const denominator = plane.normal.dot( this.direction );
  
  		if ( denominator * distToPoint < 0 ) {
  
  			return true;
  
  		}
  
  		// ray origin is behind the plane (and is pointing behind it)
  
  		return false;
  
  	}
  
  	intersectBox( box, target ) {
  
  		let tmin, tmax, tymin, tymax, tzmin, tzmax;
  
  		const invdirx = 1 / this.direction.x,
  			invdiry = 1 / this.direction.y,
  			invdirz = 1 / this.direction.z;
  
  		const origin = this.origin;
  
  		if ( invdirx >= 0 ) {
  
  			tmin = ( box.min.x - origin.x ) * invdirx;
  			tmax = ( box.max.x - origin.x ) * invdirx;
  
  		} else {
  
  			tmin = ( box.max.x - origin.x ) * invdirx;
  			tmax = ( box.min.x - origin.x ) * invdirx;
  
  		}
  
  		if ( invdiry >= 0 ) {
  
  			tymin = ( box.min.y - origin.y ) * invdiry;
  			tymax = ( box.max.y - origin.y ) * invdiry;
  
  		} else {
  
  			tymin = ( box.max.y - origin.y ) * invdiry;
  			tymax = ( box.min.y - origin.y ) * invdiry;
  
  		}
  
  		if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null;
  
  		// These lines also handle the case where tmin or tmax is NaN
  		// (result of 0 * Infinity). x !== x returns true if x is NaN
  
  		if ( tymin > tmin || tmin !== tmin ) tmin = tymin;
  
  		if ( tymax < tmax || tmax !== tmax ) tmax = tymax;
  
  		if ( invdirz >= 0 ) {
  
  			tzmin = ( box.min.z - origin.z ) * invdirz;
  			tzmax = ( box.max.z - origin.z ) * invdirz;
  
  		} else {
  
  			tzmin = ( box.max.z - origin.z ) * invdirz;
  			tzmax = ( box.min.z - origin.z ) * invdirz;
  
  		}
  
  		if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null;
  
  		if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin;
  
  		if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax;
  
  		//return point closest to the ray (positive side)
  
  		if ( tmax < 0 ) return null;
  
  		return this.at( tmin >= 0 ? tmin : tmax, target );
  
  	}
  
  	intersectsBox( box ) {
  
  		return this.intersectBox( box, _vector ) !== null;
  
  	}
  
  	intersectTriangle( a, b, c, backfaceCulling, target ) {
  
  		// Compute the offset origin, edges, and normal.
  
  		// from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h
  
  		_edge1.subVectors( b, a );
  		_edge2.subVectors( c, a );
  		_normal.crossVectors( _edge1, _edge2 );
  
  		// Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction,
  		// E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by
  		//   |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))
  		//   |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))
  		//   |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)
  		let DdN = this.direction.dot( _normal );
  		let sign;
  
  		if ( DdN > 0 ) {
  
  			if ( backfaceCulling ) return null;
  			sign = 1;
  
  		} else if ( DdN < 0 ) {
  
  			sign = - 1;
  			DdN = - DdN;
  
  		} else {
  
  			return null;
  
  		}
  
  		_diff.subVectors( this.origin, a );
  		const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) );
  
  		// b1 < 0, no intersection
  		if ( DdQxE2 < 0 ) {
  
  			return null;
  
  		}
  
  		const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) );
  
  		// b2 < 0, no intersection
  		if ( DdE1xQ < 0 ) {
  
  			return null;
  
  		}
  
  		// b1+b2 > 1, no intersection
  		if ( DdQxE2 + DdE1xQ > DdN ) {
  
  			return null;
  
  		}
  
  		// Line intersects triangle, check if ray does.
  		const QdN = - sign * _diff.dot( _normal );
  
  		// t < 0, no intersection
  		if ( QdN < 0 ) {
  
  			return null;
  
  		}
  
  		// Ray intersects triangle.
  		return this.at( QdN / DdN, target );
  
  	}
  
  	applyMatrix4( matrix4 ) {
  
  		this.origin.applyMatrix4( matrix4 );
  		this.direction.transformDirection( matrix4 );
  
  		return this;
  
  	}
  
  	equals( ray ) {
  
  		return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction );
  
  	}
  
  	clone() {
  
  		return new this.constructor().copy( this );
  
  	}
  
  }
  
  export { Ray };
  