# LineBVH

class in `three-mesh-bvh`, extends `LineLoopBVH`

```js
import { LineBVH } from 'three-mesh-bvh';
```

BVH for `THREE.Line` geometries. Like `LineLoopBVH` but excludes the final closing
segment so the open line is accurately represented.

Example: Casting rays at a million line segments every frame

```js
import { BufferAttribute, BufferGeometry, Color, Line, LineBasicMaterial, LineSegments, Points, PointsMaterial, Raycaster, Vector3 } from 'three';
import { computeBoundsTree, acceleratedRaycast, LineBVH } from 'three-mesh-bvh';

// scene, camera and renderer are initialized here

const RAYS = 500;
const SEGMENTS = 1e6;

BufferGeometry.prototype.computeBoundsTree = computeBoundsTree;
Line.prototype.raycast = acceleratedRaycast;

function getSurfacePoint( t, target ) {

	// Torus knot parameters
	const p = 3; // number of times the knot winds around the torus longitudinally
	const q = 10; // number of times the knot winds around the torus meridionally
	const R = 1.0; // major radius
	const r = 0.4; // minor radius (tube radius)

	const theta = t * Math.PI * 2;
	const phi = p * theta;
	const psi = q * theta;

	const x = ( R + r * Math.cos( psi ) ) * Math.cos( phi );
	const y = ( R + r * Math.cos( psi ) ) * Math.sin( phi );
	const z = r * Math.sin( psi );

	target.set( x, y, z );

}

const positions = new Float32Array( SEGMENTS * 3 );
const colors = new Float32Array( SEGMENTS * 3 );
const norm = new Vector3();
const tangent = new Vector3();
const v0 = new Vector3();
const v1 = new Vector3();
const color = new Color();
for ( let i = 0; i < SEGMENTS; i ++ ) {

	const t = i / SEGMENTS;
	getSurfacePoint( t, v0 );
	getSurfacePoint( t + 1e-4, v1 );

	norm.copy( v0 ).normalize();
	tangent.subVectors( v1, v0 ).normalize();
	norm.applyAxisAngle( tangent, 1000 * t * 2 * Math.PI );

	v0.addScaledVector( norm, 0.05 * ( Math.sin( 50 * t * Math.PI ) + 2 ) );
	v0.toArray( positions, 3 * i );
	color.setHSL( t * 3, 1.0, 0.6 ).toArray( colors, 3 * i );

}

const geometry = new BufferGeometry();
geometry.setAttribute( 'position', new BufferAttribute( positions, 3 ) );
geometry.setAttribute( 'color', new BufferAttribute( colors, 3 ) );
geometry.computeBoundsTree( { type: LineBVH } );

scene.background = new Color( 0x131619 );

const line = new Line( geometry, new LineBasicMaterial( { vertexColors: true, linewidth: 2 } ) );
scene.add( line );

const origins = new Array( RAYS ).fill().map( () => new Vector3().randomDirection().multiplyScalar( 2.5 ) );
const rays = new BufferGeometry();
rays.setAttribute( 'position', new BufferAttribute( new Float32Array( RAYS * 6 ), 3 ) );
scene.add(
	new LineSegments( rays, new LineBasicMaterial( { color: 0xffffff, transparent: true, opacity: 0.25 } ) ),
	new Points( rays, new PointsMaterial( { color: 0xffffff, size: 0.03 } ) ),
);

const raycaster = new Raycaster();
raycaster.firstHitOnly = true;
raycaster.params.Line.threshold = 0.01;
const { ray } = raycaster;
const hits = [];
renderer.setAnimationLoop( time => {

	line.rotation.y = time * 0.00012;
	line.updateMatrixWorld();

	const position = rays.attributes.position;
	for ( let i = 0; i < RAYS; i ++ ) {

		ray.origin.copy( origins[ i ] );
		ray.direction.copy( ray.origin ).negate().normalize();
		hits.length = 0;
		raycaster.intersectObject( line, false, hits );
		const end = hits.length ? hits[ 0 ].point : ray.origin;
		position.setXYZ( 2 * i, ray.origin.x, ray.origin.y, ray.origin.z );
		position.setXYZ( 2 * i + 1, end.x, end.y, end.z );

	}

	position.needsUpdate = true;
	renderer.render( scene, camera );

} );
```

## Constructor

```js
new LineBVH( geometry: BufferGeometry, options?: Object )
```

- `geometry`, `BufferGeometry`
- `options`, `Object`, optional: Same options as `GeometryBVH`. `indirect` is always forced to `true`.
