Skip to content

⬅️ Back to Table of Contents

📄 LineSegments2.js

📊 Analysis Summary

Metric Count
🔧 Functions 6
🧱 Classes 1
📦 Imports 13
📊 Variables & Constants 51

📚 Table of Contents

🛠️ File Location:

📂 examples/jsm/lines/webgpu/LineSegments2.js

📦 Imports

Name Source
Box3 three/webgpu
InstancedInterleavedBuffer three/webgpu
InterleavedBufferAttribute three/webgpu
Line3 three/webgpu
MathUtils three/webgpu
Matrix4 three/webgpu
Mesh three/webgpu
Sphere three/webgpu
Vector3 three/webgpu
Vector4 three/webgpu
Line2NodeMaterial three/webgpu
Vector2 three/webgpu
LineSegmentsGeometry ../../lines/LineSegmentsGeometry.js

Variables & Constants

Name Type Kind Value Exported
_start any let/var new Vector3()
_end any let/var new Vector3()
_start4 any let/var new Vector4()
_end4 any let/var new Vector4()
_ssOrigin any let/var new Vector4()
_ssOrigin3 any let/var new Vector3()
_mvMatrix any let/var new Matrix4()
_line any let/var new Line3()
_closestPoint any let/var new Vector3()
_box any let/var new Box3()
_sphere any let/var new Sphere()
_clipToWorldVector any let/var new Vector4()
_viewport any let/var new Vector4()
_ray any let/var *not shown*
_lineWidth any let/var *not shown*
matrixWorld any let/var lineSegments.matrixWorld
geometry any let/var lineSegments.geometry
instanceStart any let/var geometry.attributes.instanceStart
instanceEnd any let/var geometry.attributes.instanceEnd
pointOnLine any let/var new Vector3()
point any let/var new Vector3()
isInside boolean let/var point.distanceTo( pointOnLine ) < _lineWidth * 0.5
projectionMatrix any let/var camera.projectionMatrix
matrixWorld any let/var lineSegments.matrixWorld
resolution any let/var lineSegments._resolution
geometry any let/var lineSegments.geometry
instanceStart any let/var geometry.attributes.instanceStart
instanceEnd any let/var geometry.attributes.instanceEnd
near number let/var - camera.near
isBehindCameraNear boolean let/var _start4.z > near && _end4.z > near
deltaDist number let/var _start4.z - _end4.z
t number let/var ( _start4.z - near ) / deltaDist
deltaDist number let/var _end4.z - _start4.z
t number let/var ( _end4.z - near ) / deltaDist
isInClipSpace boolean let/var zPos >= - 1 && zPos <= 1
isInside boolean let/var _ssOrigin3.distanceTo( _closestPoint ) < _lineWidth * 0.5
pointOnLine any let/var new Vector3()
point any let/var new Vector3()
geometry any let/var this.geometry
instanceStart any let/var geometry.attributes.instanceStart
instanceEnd any let/var geometry.attributes.instanceEnd
lineDistances Float32Array<ArrayBuffer> let/var new Float32Array( 2 * instanceStart.count )
instanceDistanceBuffer any let/var new InstancedInterleavedBuffer( lineDistances, 2, 1 )
worldUnits any let/var this.material.worldUnits
camera any let/var raycaster.camera
threshold any let/var ( raycaster.params.Line2 !== undefined ) ? raycaster.params.Line2.threshold \...
matrixWorld any let/var this.matrixWorld
geometry any let/var this.geometry
material any let/var this.material
sphereMargin any let/var *not shown*
boxMargin any let/var *not shown*

Functions

getWorldSpaceHalfWidth(camera: any, distance: any, resolution: any): number

Parameters:

  • camera any
  • distance any
  • resolution any

Returns: number

Calls:

  • _clipToWorldVector.set( 0, 0, - distance, 1.0 ).applyMatrix4
  • _clipToWorldVector.multiplyScalar
  • _clipToWorldVector.applyMatrix4
  • Math.abs
  • Math.max

Internal Comments:

// transform into clip space, adjust the x and y values by the pixel width offset, then (x6)
// transform back into world space to get world offset. Note clip space is [-1, 1] so full (x6)
// width does not need to be halved. (x6)

Code
function getWorldSpaceHalfWidth( camera, distance, resolution ) {

    // transform into clip space, adjust the x and y values by the pixel width offset, then
    // transform back into world space to get world offset. Note clip space is [-1, 1] so full
    // width does not need to be halved.
    _clipToWorldVector.set( 0, 0, - distance, 1.0 ).applyMatrix4( camera.projectionMatrix );
    _clipToWorldVector.multiplyScalar( 1.0 / _clipToWorldVector.w );
    _clipToWorldVector.x = _lineWidth / resolution.width;
    _clipToWorldVector.y = _lineWidth / resolution.height;
    _clipToWorldVector.applyMatrix4( camera.projectionMatrixInverse );
    _clipToWorldVector.multiplyScalar( 1.0 / _clipToWorldVector.w );

    return Math.abs( Math.max( _clipToWorldVector.x, _clipToWorldVector.y ) );

}

raycastWorldUnits(lineSegments: any, intersects: any): void

Parameters:

  • lineSegments any
  • intersects any

Returns: void

Calls:

  • Math.min
  • _line.start.fromBufferAttribute
  • _line.end.fromBufferAttribute
  • _line.applyMatrix4
  • _ray.distanceSqToSegment
  • point.distanceTo
  • intersects.push
  • _ray.origin.distanceTo
Code
function raycastWorldUnits( lineSegments, intersects ) {

    const matrixWorld = lineSegments.matrixWorld;
    const geometry = lineSegments.geometry;
    const instanceStart = geometry.attributes.instanceStart;
    const instanceEnd = geometry.attributes.instanceEnd;
    const segmentCount = Math.min( geometry.instanceCount, instanceStart.count );

    for ( let i = 0, l = segmentCount; i < l; i ++ ) {

        _line.start.fromBufferAttribute( instanceStart, i );
        _line.end.fromBufferAttribute( instanceEnd, i );

        _line.applyMatrix4( matrixWorld );

        const pointOnLine = new Vector3();
        const point = new Vector3();

        _ray.distanceSqToSegment( _line.start, _line.end, point, pointOnLine );
        const isInside = point.distanceTo( pointOnLine ) < _lineWidth * 0.5;

        if ( isInside ) {

            intersects.push( {
                point,
                pointOnLine,
                distance: _ray.origin.distanceTo( point ),
                object: lineSegments,
                face: null,
                faceIndex: i,
                uv: null,
                uv1: null,
            } );

        }

    }

}

raycastScreenSpace(lineSegments: any, camera: any, intersects: any): void

Parameters:

  • lineSegments any
  • camera any
  • intersects any

Returns: void

Calls:

  • Math.min
  • _ray.at
  • _ssOrigin.applyMatrix4
  • _ssOrigin.multiplyScalar
  • _ssOrigin3.copy
  • _mvMatrix.multiplyMatrices
  • _start4.fromBufferAttribute
  • _end4.fromBufferAttribute
  • _start4.applyMatrix4
  • _end4.applyMatrix4
  • _start4.lerp
  • _end4.lerp
  • _start4.multiplyScalar
  • _end4.multiplyScalar
  • _line.start.copy
  • _line.end.copy
  • _line.closestPointToPointParameter
  • _line.at
  • MathUtils.lerp
  • _ssOrigin3.distanceTo
  • _line.start.fromBufferAttribute
  • _line.end.fromBufferAttribute
  • _line.start.applyMatrix4
  • _line.end.applyMatrix4
  • _ray.distanceSqToSegment
  • intersects.push
  • _ray.origin.distanceTo

Internal Comments:

// (x4)
// pick a point 1 unit out along the ray to avoid the ray origin (x4)
// sitting at the camera origin which will cause "w" to be 0 when (x4)
// applying the projection matrix. (x4)
// ndc space [ - 1.0, 1.0 ] (x8)
// screen space (x8)
// camera space (x4)
// skip the segment if it's entirely behind the camera (x2)
// trim the segment if it extends behind camera near
// clip space (x4)
// create 2d segment (x5)
// get closest point on ray to segment (x2)
// check if the intersection point is within clip space (x2)

Code
function raycastScreenSpace( lineSegments, camera, intersects ) {

    const projectionMatrix = camera.projectionMatrix;
    const matrixWorld = lineSegments.matrixWorld;

    const resolution = lineSegments._resolution;

    const geometry = lineSegments.geometry;
    const instanceStart = geometry.attributes.instanceStart;
    const instanceEnd = geometry.attributes.instanceEnd;
    const segmentCount = Math.min( geometry.instanceCount, instanceStart.count );

    const near = - camera.near;

    //

    // pick a point 1 unit out along the ray to avoid the ray origin
    // sitting at the camera origin which will cause "w" to be 0 when
    // applying the projection matrix.
    _ray.at( 1, _ssOrigin );

    // ndc space [ - 1.0, 1.0 ]
    _ssOrigin.w = 1;
    _ssOrigin.applyMatrix4( camera.matrixWorldInverse );
    _ssOrigin.applyMatrix4( projectionMatrix );
    _ssOrigin.multiplyScalar( 1 / _ssOrigin.w );

    // screen space
    _ssOrigin.x *= resolution.x / 2;
    _ssOrigin.y *= resolution.y / 2;
    _ssOrigin.z = 0;

    _ssOrigin3.copy( _ssOrigin );

    _mvMatrix.multiplyMatrices( camera.matrixWorldInverse, matrixWorld );

    for ( let i = 0, l = segmentCount; i < l; i ++ ) {

        _start4.fromBufferAttribute( instanceStart, i );
        _end4.fromBufferAttribute( instanceEnd, i );

        _start4.w = 1;
        _end4.w = 1;

        // camera space
        _start4.applyMatrix4( _mvMatrix );
        _end4.applyMatrix4( _mvMatrix );

        // skip the segment if it's entirely behind the camera
        const isBehindCameraNear = _start4.z > near && _end4.z > near;
        if ( isBehindCameraNear ) {

            continue;

        }

        // trim the segment if it extends behind camera near
        if ( _start4.z > near ) {

            const deltaDist = _start4.z - _end4.z;
            const t = ( _start4.z - near ) / deltaDist;
            _start4.lerp( _end4, t );

        } else if ( _end4.z > near ) {

            const deltaDist = _end4.z - _start4.z;
            const t = ( _end4.z - near ) / deltaDist;
            _end4.lerp( _start4, t );

        }

        // clip space
        _start4.applyMatrix4( projectionMatrix );
        _end4.applyMatrix4( projectionMatrix );

        // ndc space [ - 1.0, 1.0 ]
        _start4.multiplyScalar( 1 / _start4.w );
        _end4.multiplyScalar( 1 / _end4.w );

        // screen space
        _start4.x *= resolution.x / 2;
        _start4.y *= resolution.y / 2;

        _end4.x *= resolution.x / 2;
        _end4.y *= resolution.y / 2;

        // create 2d segment
        _line.start.copy( _start4 );
        _line.start.z = 0;

        _line.end.copy( _end4 );
        _line.end.z = 0;

        // get closest point on ray to segment
        const param = _line.closestPointToPointParameter( _ssOrigin3, true );
        _line.at( param, _closestPoint );

        // check if the intersection point is within clip space
        const zPos = MathUtils.lerp( _start4.z, _end4.z, param );
        const isInClipSpace = zPos >= - 1 && zPos <= 1;

        const isInside = _ssOrigin3.distanceTo( _closestPoint ) < _lineWidth * 0.5;

        if ( isInClipSpace && isInside ) {

            _line.start.fromBufferAttribute( instanceStart, i );
            _line.end.fromBufferAttribute( instanceEnd, i );

            _line.start.applyMatrix4( matrixWorld );
            _line.end.applyMatrix4( matrixWorld );

            const pointOnLine = new Vector3();
            const point = new Vector3();

            _ray.distanceSqToSegment( _line.start, _line.end, point, pointOnLine );

            intersects.push( {
                point: point,
                pointOnLine: pointOnLine,
                distance: _ray.origin.distanceTo( point ),
                object: lineSegments,
                face: null,
                faceIndex: i,
                uv: null,
                uv1: null,
            } );

        }

    }

}

LineSegments2.computeLineDistances(): LineSegments2

JSDoc:

/**
     * Computes an array of distance values which are necessary for rendering dashed lines.
     * For each vertex in the geometry, the method calculates the cumulative length from the
     * current point to the very beginning of the line.
     *
     * @return {LineSegments2} A reference to this instance.
     */

Returns: LineSegments2

Calls:

  • _start.fromBufferAttribute
  • _end.fromBufferAttribute
  • _start.distanceTo
  • geometry.setAttribute

Internal Comments:

// for backwards-compatibility, but could be a method of LineSegmentsGeometry... (x2)

Code
computeLineDistances() {

        // for backwards-compatibility, but could be a method of LineSegmentsGeometry...

        const geometry = this.geometry;

        const instanceStart = geometry.attributes.instanceStart;
        const instanceEnd = geometry.attributes.instanceEnd;
        const lineDistances = new Float32Array( 2 * instanceStart.count );

        for ( let i = 0, j = 0, l = instanceStart.count; i < l; i ++, j += 2 ) {

            _start.fromBufferAttribute( instanceStart, i );
            _end.fromBufferAttribute( instanceEnd, i );

            lineDistances[ j ] = ( j === 0 ) ? 0 : lineDistances[ j - 1 ];
            lineDistances[ j + 1 ] = lineDistances[ j ] + _start.distanceTo( _end );

        }

        const instanceDistanceBuffer = new InstancedInterleavedBuffer( lineDistances, 2, 1 ); // d0, d1

        geometry.setAttribute( 'instanceDistanceStart', new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 0 ) ); // d0
        geometry.setAttribute( 'instanceDistanceEnd', new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 1 ) ); // d1

        return this;

    }

LineSegments2.onBeforeRender(renderer: any): void

Parameters:

  • renderer any

Returns: void

Calls:

  • renderer.getViewport
  • this._resolution.set
Code
onBeforeRender( renderer ) {

        renderer.getViewport( _viewport );
        this._resolution.set( _viewport.z, _viewport.w );

    }

LineSegments2.raycast(raycaster: Raycaster, intersects: any[]): void

JSDoc:

/**
     * Computes intersection points between a casted ray and this instance.
     *
     * @param {Raycaster} raycaster - The raycaster.
     * @param {Array<Object>} intersects - The target array that holds the intersection points.
     */

Parameters:

  • raycaster Raycaster
  • intersects any[]

Returns: void

Calls:

  • console.error
  • geometry.computeBoundingSphere
  • _sphere.copy( geometry.boundingSphere ).applyMatrix4
  • Math.max
  • _sphere.distanceToPoint
  • getWorldSpaceHalfWidth
  • _ray.intersectsSphere
  • geometry.computeBoundingBox
  • _box.copy( geometry.boundingBox ).applyMatrix4
  • _box.distanceToPoint
  • _box.expandByScalar
  • _ray.intersectsBox
  • raycastWorldUnits
  • raycastScreenSpace

Internal Comments:

// check if we intersect the sphere bounds
// increase the sphere bounds by the worst case line screen space width (x2)
// check if we intersect the box bounds
// increase the box bounds by the worst case line width (x2)

Code
raycast( raycaster, intersects ) {

        const worldUnits = this.material.worldUnits;
        const camera = raycaster.camera;

        if ( camera === null && ! worldUnits ) {

            console.error( 'LineSegments2: "Raycaster.camera" needs to be set in order to raycast against LineSegments2 while worldUnits is set to false.' );

        }

        const threshold = ( raycaster.params.Line2 !== undefined ) ? raycaster.params.Line2.threshold || 0 : 0;

        _ray = raycaster.ray;

        const matrixWorld = this.matrixWorld;
        const geometry = this.geometry;
        const material = this.material;

        _lineWidth = material.linewidth + threshold;

        // check if we intersect the sphere bounds
        if ( geometry.boundingSphere === null ) {

            geometry.computeBoundingSphere();

        }

        _sphere.copy( geometry.boundingSphere ).applyMatrix4( matrixWorld );

        // increase the sphere bounds by the worst case line screen space width
        let sphereMargin;
        if ( worldUnits ) {

            sphereMargin = _lineWidth * 0.5;

        } else {

            const distanceToSphere = Math.max( camera.near, _sphere.distanceToPoint( _ray.origin ) );
            sphereMargin = getWorldSpaceHalfWidth( camera, distanceToSphere, this._resolution );

        }

        _sphere.radius += sphereMargin;

        if ( _ray.intersectsSphere( _sphere ) === false ) {

            return;

        }

        // check if we intersect the box bounds
        if ( geometry.boundingBox === null ) {

            geometry.computeBoundingBox();

        }

        _box.copy( geometry.boundingBox ).applyMatrix4( matrixWorld );

        // increase the box bounds by the worst case line width
        let boxMargin;
        if ( worldUnits ) {

            boxMargin = _lineWidth * 0.5;

        } else {

            const distanceToBox = Math.max( camera.near, _box.distanceToPoint( _ray.origin ) );
            boxMargin = getWorldSpaceHalfWidth( camera, distanceToBox, this._resolution );

        }

        _box.expandByScalar( boxMargin );

        if ( _ray.intersectsBox( _box ) === false ) {

            return;

        }

        if ( worldUnits ) {

            raycastWorldUnits( this, intersects );

        } else {

            raycastScreenSpace( this, camera, intersects );

        }

    }

Classes

LineSegments2

Class Code
class LineSegments2 extends Mesh {

    /**
     * Constructs a new wide line.
     *
     * @param {LineSegmentsGeometry} [geometry] - The line geometry.
     * @param {Line2NodeMaterial} [material] - The line material.
     */
    constructor( geometry = new LineSegmentsGeometry(), material = new Line2NodeMaterial( { color: Math.random() * 0xffffff } ) ) {

        super( geometry, material );

        /**
         * This flag can be used for type testing.
         *
         * @type {boolean}
         * @readonly
         * @default true
         */
        this.isLineSegments2 = true;

        this.type = 'LineSegments2';

        this._resolution = new Vector2();

    }

    /**
     * Computes an array of distance values which are necessary for rendering dashed lines.
     * For each vertex in the geometry, the method calculates the cumulative length from the
     * current point to the very beginning of the line.
     *
     * @return {LineSegments2} A reference to this instance.
     */
    computeLineDistances() {

        // for backwards-compatibility, but could be a method of LineSegmentsGeometry...

        const geometry = this.geometry;

        const instanceStart = geometry.attributes.instanceStart;
        const instanceEnd = geometry.attributes.instanceEnd;
        const lineDistances = new Float32Array( 2 * instanceStart.count );

        for ( let i = 0, j = 0, l = instanceStart.count; i < l; i ++, j += 2 ) {

            _start.fromBufferAttribute( instanceStart, i );
            _end.fromBufferAttribute( instanceEnd, i );

            lineDistances[ j ] = ( j === 0 ) ? 0 : lineDistances[ j - 1 ];
            lineDistances[ j + 1 ] = lineDistances[ j ] + _start.distanceTo( _end );

        }

        const instanceDistanceBuffer = new InstancedInterleavedBuffer( lineDistances, 2, 1 ); // d0, d1

        geometry.setAttribute( 'instanceDistanceStart', new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 0 ) ); // d0
        geometry.setAttribute( 'instanceDistanceEnd', new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 1 ) ); // d1

        return this;

    }

    onBeforeRender( renderer ) {

        renderer.getViewport( _viewport );
        this._resolution.set( _viewport.z, _viewport.w );

    }

    /**
     * Computes intersection points between a casted ray and this instance.
     *
     * @param {Raycaster} raycaster - The raycaster.
     * @param {Array<Object>} intersects - The target array that holds the intersection points.
     */
    raycast( raycaster, intersects ) {

        const worldUnits = this.material.worldUnits;
        const camera = raycaster.camera;

        if ( camera === null && ! worldUnits ) {

            console.error( 'LineSegments2: "Raycaster.camera" needs to be set in order to raycast against LineSegments2 while worldUnits is set to false.' );

        }

        const threshold = ( raycaster.params.Line2 !== undefined ) ? raycaster.params.Line2.threshold || 0 : 0;

        _ray = raycaster.ray;

        const matrixWorld = this.matrixWorld;
        const geometry = this.geometry;
        const material = this.material;

        _lineWidth = material.linewidth + threshold;

        // check if we intersect the sphere bounds
        if ( geometry.boundingSphere === null ) {

            geometry.computeBoundingSphere();

        }

        _sphere.copy( geometry.boundingSphere ).applyMatrix4( matrixWorld );

        // increase the sphere bounds by the worst case line screen space width
        let sphereMargin;
        if ( worldUnits ) {

            sphereMargin = _lineWidth * 0.5;

        } else {

            const distanceToSphere = Math.max( camera.near, _sphere.distanceToPoint( _ray.origin ) );
            sphereMargin = getWorldSpaceHalfWidth( camera, distanceToSphere, this._resolution );

        }

        _sphere.radius += sphereMargin;

        if ( _ray.intersectsSphere( _sphere ) === false ) {

            return;

        }

        // check if we intersect the box bounds
        if ( geometry.boundingBox === null ) {

            geometry.computeBoundingBox();

        }

        _box.copy( geometry.boundingBox ).applyMatrix4( matrixWorld );

        // increase the box bounds by the worst case line width
        let boxMargin;
        if ( worldUnits ) {

            boxMargin = _lineWidth * 0.5;

        } else {

            const distanceToBox = Math.max( camera.near, _box.distanceToPoint( _ray.origin ) );
            boxMargin = getWorldSpaceHalfWidth( camera, distanceToBox, this._resolution );

        }

        _box.expandByScalar( boxMargin );

        if ( _ray.intersectsBox( _box ) === false ) {

            return;

        }

        if ( worldUnits ) {

            raycastWorldUnits( this, intersects );

        } else {

            raycastScreenSpace( this, camera, intersects );

        }

    }

}

Methods

computeLineDistances(): LineSegments2
Code
computeLineDistances() {

        // for backwards-compatibility, but could be a method of LineSegmentsGeometry...

        const geometry = this.geometry;

        const instanceStart = geometry.attributes.instanceStart;
        const instanceEnd = geometry.attributes.instanceEnd;
        const lineDistances = new Float32Array( 2 * instanceStart.count );

        for ( let i = 0, j = 0, l = instanceStart.count; i < l; i ++, j += 2 ) {

            _start.fromBufferAttribute( instanceStart, i );
            _end.fromBufferAttribute( instanceEnd, i );

            lineDistances[ j ] = ( j === 0 ) ? 0 : lineDistances[ j - 1 ];
            lineDistances[ j + 1 ] = lineDistances[ j ] + _start.distanceTo( _end );

        }

        const instanceDistanceBuffer = new InstancedInterleavedBuffer( lineDistances, 2, 1 ); // d0, d1

        geometry.setAttribute( 'instanceDistanceStart', new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 0 ) ); // d0
        geometry.setAttribute( 'instanceDistanceEnd', new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 1 ) ); // d1

        return this;

    }
onBeforeRender(renderer: any): void
Code
onBeforeRender( renderer ) {

        renderer.getViewport( _viewport );
        this._resolution.set( _viewport.z, _viewport.w );

    }
raycast(raycaster: Raycaster, intersects: any[]): void
Code
raycast( raycaster, intersects ) {

        const worldUnits = this.material.worldUnits;
        const camera = raycaster.camera;

        if ( camera === null && ! worldUnits ) {

            console.error( 'LineSegments2: "Raycaster.camera" needs to be set in order to raycast against LineSegments2 while worldUnits is set to false.' );

        }

        const threshold = ( raycaster.params.Line2 !== undefined ) ? raycaster.params.Line2.threshold || 0 : 0;

        _ray = raycaster.ray;

        const matrixWorld = this.matrixWorld;
        const geometry = this.geometry;
        const material = this.material;

        _lineWidth = material.linewidth + threshold;

        // check if we intersect the sphere bounds
        if ( geometry.boundingSphere === null ) {

            geometry.computeBoundingSphere();

        }

        _sphere.copy( geometry.boundingSphere ).applyMatrix4( matrixWorld );

        // increase the sphere bounds by the worst case line screen space width
        let sphereMargin;
        if ( worldUnits ) {

            sphereMargin = _lineWidth * 0.5;

        } else {

            const distanceToSphere = Math.max( camera.near, _sphere.distanceToPoint( _ray.origin ) );
            sphereMargin = getWorldSpaceHalfWidth( camera, distanceToSphere, this._resolution );

        }

        _sphere.radius += sphereMargin;

        if ( _ray.intersectsSphere( _sphere ) === false ) {

            return;

        }

        // check if we intersect the box bounds
        if ( geometry.boundingBox === null ) {

            geometry.computeBoundingBox();

        }

        _box.copy( geometry.boundingBox ).applyMatrix4( matrixWorld );

        // increase the box bounds by the worst case line width
        let boxMargin;
        if ( worldUnits ) {

            boxMargin = _lineWidth * 0.5;

        } else {

            const distanceToBox = Math.max( camera.near, _box.distanceToPoint( _ray.origin ) );
            boxMargin = getWorldSpaceHalfWidth( camera, distanceToBox, this._resolution );

        }

        _box.expandByScalar( boxMargin );

        if ( _ray.intersectsBox( _box ) === false ) {

            return;

        }

        if ( worldUnits ) {

            raycastWorldUnits( this, intersects );

        } else {

            raycastScreenSpace( this, camera, intersects );

        }

    }