Skip to content

Instantly share code, notes, and snippets.

@gstreetmedia
Created November 12, 2025 19:10
Show Gist options
  • Select an option

  • Save gstreetmedia/3eb43b6f95ffc43f93039134edd56198 to your computer and use it in GitHub Desktop.

Select an option

Save gstreetmedia/3eb43b6f95ffc43f93039134edd56198 to your computer and use it in GitHub Desktop.
ThreeJS Box3 subtract Method
/**
* Subtract boxB from boxA, returning the remaining non-overlapping regions
* This properly handles Box3 subtraction by breaking the result into slices
* @param boxA - The box to subtract from
* @param boxB - The box to subtract
* @returns Array of Box3 objects representing the remaining volume after subtraction
*/
export function subtractBox3(boxA: Box3, boxB: Box3): Box3[] {
const result: Box3[] = [];
// Check if boxes actually intersect
const intersection = new Box3();
intersection.copy(boxA);
// Manually check intersection without modifying boxes
if (boxA.max.x <= boxB.min.x || boxA.min.x >= boxB.max.x ||
boxA.max.y <= boxB.min.y || boxA.min.y >= boxB.max.y ||
boxA.max.z <= boxB.min.z || boxA.min.z >= boxB.max.z) {
// No intersection, return original box
return [boxA.clone()];
}
// Calculate actual intersection bounds
const intMin = {
x: Math.max(boxA.min.x, boxB.min.x),
y: Math.max(boxA.min.y, boxB.min.y),
z: Math.max(boxA.min.z, boxB.min.z)
};
const intMax = {
x: Math.min(boxA.max.x, boxB.max.x),
y: Math.min(boxA.max.y, boxB.max.y),
z: Math.min(boxA.max.z, boxB.max.z)
};
// Bottom slice (min Y)
if (boxA.min.y < intMin.y) {
result.push(new Box3(
new Vector3(boxA.min.x, boxA.min.y, boxA.min.z),
new Vector3(boxA.max.x, intMin.y, boxA.max.z)
));
}
// Top slice (max Y)
if (boxA.max.y > intMax.y) {
result.push(new Box3(
new Vector3(boxA.min.x, intMax.y, boxA.min.z),
new Vector3(boxA.max.x, boxA.max.y, boxA.max.z)
));
}
// Left slice (min X) - only in the intersection Y/Z region
if (boxA.min.x < intMin.x) {
result.push(new Box3(
new Vector3(boxA.min.x, intMin.y, boxA.min.z),
new Vector3(intMin.x, intMax.y, boxA.max.z)
));
}
// Right slice (max X) - only in the intersection Y/Z region
if (boxA.max.x > intMax.x) {
result.push(new Box3(
new Vector3(intMax.x, intMin.y, boxA.min.z),
new Vector3(boxA.max.x, intMax.y, boxA.max.z)
));
}
// Front slice (min Z) - only in the intersection X/Y region
if (boxA.min.z < intMin.z) {
result.push(new Box3(
new Vector3(intMin.x, intMin.y, boxA.min.z),
new Vector3(intMax.x, intMax.y, intMin.z)
));
}
// Back slice (max Z) - only in the intersection X/Y region
if (boxA.max.z > intMax.z) {
result.push(new Box3(
new Vector3(intMin.x, intMin.y, intMax.z),
new Vector3(intMax.x, intMax.y, boxA.max.z)
));
}
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment