function computeSelectedTriangles( mesh, camera, selectionTool, params ) {

	// TODO: Possible improvements
	// - Correctly handle the camera near clip
	// - Improve line line intersect performance?

	toScreenSpaceMatrix
		.copy( mesh.matrixWorld )
		.premultiply( camera.matrixWorldInverse )
		.premultiply( camera.projectionMatrix );

	invWorldMatrix.copy( mesh.matrixWorld ).invert();
	camLocalPosition
		.set( 0, 0, 0 )
		.applyMatrix4( camera.matrixWorld )
		.applyMatrix4( invWorldMatrix );

	const lassoSegments = connectPointsWithLines(
		convertTripletsToPoints( selectionTool.points )
	);

	/**
	 * Per-depth cache of lasso segments that were filtered to be to the right of a box for that depth.
	 * @type {Array<Array<THREE.Line3>>}
	 */
	const perBoundsSegmentCache = [];

	/**
	 * Array of triplets representing indices of vertices of selected triangles.
	 * @type {Array<number>}
	 */
	const indices = [];

	// find all the triangles in the mesh that intersect the lasso
	mesh.geometry.boundsTree.shapecast( {
		intersectsBounds: ( box, isLeaf, score, depth ) => {

			// check if the bounds are intersected or contained by the lasso region to narrow down on the triangles

			if ( ! params.useBoundsTree ) {

				return INTERSECTED;

			}

			const projectedBoxPoints = extractBoxVertices( box, boxPoints ).map( ( v ) =>
				v.applyMatrix4( toScreenSpaceMatrix )
			);

			let minY = Infinity;
			let maxY = - Infinity;
			let minX = Infinity;
			for ( const point of projectedBoxPoints ) {

				if ( point.y < minY ) minY = point.y;
				if ( point.y > maxY ) maxY = point.y;
				if ( point.x < minX ) minX = point.x;

			}

			// filter the lasso segments to remove the ones completely to the left, above, or below the bounding box.
			// we don't need the ones on the left because the point-in-polygon ray casting algorithm casts rays to the right.
			// cache the filtered segments in the above array for subsequent child checks to use.
			const parentSegments = perBoundsSegmentCache[ depth - 1 ] || lassoSegments;
			const segmentsToCheck = parentSegments.filter( ( segment ) =>
				isSegmentToTheRight( segment, minX, minY, maxY )
			);
			perBoundsSegmentCache[ depth ] = segmentsToCheck;

			if ( segmentsToCheck.length === 0 ) {

				return NOT_INTERSECTED;

			}

			const hull = getConvexHull( projectedBoxPoints );
			const hullSegments = connectPointsWithLines( hull, boxLines );

			// If any lasso point is inside the hull (arbitrarily checking the first) then the bounds are intersected by the lasso.
			if ( isPointInsidePolygon( segmentsToCheck[ 0 ].start, hullSegments ) ) {

				return INTERSECTED;

			}

			// if any hull segment is intersected by any lasso segment then the bounds are intersected by the lasso
			for ( const hullSegment of hullSegments ) {

				for ( const selectionSegment of segmentsToCheck ) {

					if ( lineCrossesLine( hullSegment, selectionSegment ) ) {

						return INTERSECTED;

					}

				}

			}

			// No lasso segments intersected the bounds, and at least the first point is definitely outside the hull,
			// so either the entire hull is inside the lasso, or the lasso is somewhere different and does not touch the hull.
			return isPointInsidePolygon( hull[ 0 ], segmentsToCheck ) ? CONTAINED : NOT_INTERSECTED;

		},

		intersectsTriangle: ( tri, index, contained, depth ) => {

			// if the box containing this triangle was intersected or contained, check if the triangle itself should be selected

			const i3 = index * 3;
			const a = i3 + 0;
			const b = i3 + 1;
			const c = i3 + 2;

			// check all the segments if using no bounds tree
			const segmentsToCheck = params.useBoundsTree
				? perBoundsSegmentCache[ depth ]
				: lassoSegments;
			if (
				params.selectionMode === "centroid" ||
				params.selectionMode === "centroid-visible"
			) {

				// get the center of the triangle
				centroid
					.copy( tri.a )
					.add( tri.b )
					.add( tri.c )
					.multiplyScalar( 1 / 3 );
				screenCentroid.copy( centroid ).applyMatrix4( toScreenSpaceMatrix );

				if (
					contained ||
					isPointInsidePolygon( screenCentroid, segmentsToCheck )
				) {

					// if we're only selecting visible faces then perform a ray check to ensure the centroid
					// is visible.
					if ( params.selectionMode === "centroid-visible" ) {

						tri.getNormal( faceNormal );
						tempRay.origin.copy( centroid ).addScaledVector( faceNormal, 1e-6 );
						tempRay.direction.subVectors( camLocalPosition, centroid );

						const res = mesh.geometry.boundsTree.raycastFirst(
							tempRay,
							THREE.DoubleSide
						);
						if ( res ) {

							return false;

						}

					}

					indices.push( a, b, c );
					return params.selectWholeModel;

				}

			} else if ( params.selectionMode === "intersection" ) {

				// if the parent bounds were marked as contained then we contain all the triangles within
				if ( contained ) {

					indices.push( a, b, c );
					return params.selectWholeModel;

				}

				// check if any of the projected vertices are inside the selection and if so then the triangle is selected
				const projectedTriangle = [ tri.a, tri.b, tri.c ].map( ( v ) =>
					v.applyMatrix4( toScreenSpaceMatrix )
				);
				for ( const point of projectedTriangle ) {

					if ( isPointInsidePolygon( point, segmentsToCheck ) ) {

						indices.push( a, b, c );
						return params.selectWholeModel;

					}

				}

				// check for the case where a selection intersects a triangle but does not contain any
				// of the vertices
				const triangleSegments = connectPointsWithLines(
					projectedTriangle,
					boxLines
				);
				for ( const segment of triangleSegments ) {

					for ( const selectionSegment of segmentsToCheck ) {

						if ( lineCrossesLine( segment, selectionSegment ) ) {

							indices.push( a, b, c );
							return params.selectWholeModel;

						}

					}

				}

			}

			return false;

		},
	} );

	return indices;

}
Compute selected triangles:

1. Construct a list of screen space line segments that represent the shape drawn by the user.
2. For every triangle in the geometry check if any part is within the shape. If it is then consider the triangle selected.

@returns Array of triplets representing indices of vertices of selected triangles

@see https://github.com/gkjohnson/three-mesh-bvh/issues/166#issuecomment-752194034