

How would I negate block like parts, without using unions but instead splitting it into other parts as shown above?


How would I negate block like parts, without using unions but instead splitting it into other parts as shown above?
EDIT: Okay, I posted the second part that actually solves what you were asking for
What a fun problem! Hope this is of some help.
EDIT: I swapped ∪ for ∩ >.<’ it’s fixed now tho
function corner1(part) return part.Position - part.Size / 2 end function corner2(part) return part.Position + part.Size / 2 end function minMax(v1, v2) return math.min(v1, v2), math.max(v1, v2) end function avg(v1, v2) return (v1 + v2) / 2 end --[[ Given two number ranges (p11, p12) and (p21, p22), return the intersection of those ranges. ]] function numbersIntersection(p11: number, p12: number, p21: number, p22: number): {number} --Ensure segments (p11, p12) and (p21, p22) go from small to large --Ensure p11 comes before p12 p11, p12 = minMax(p11, p12) --Ensure p21 comes before p22 p21, p22 = minMax(p21, p22) --Ensure (p11, p12) starts before (p21, p22) if p11 > p21 then --... by swapping if that's not the case p11, p12, p21, p22 = p21, p22, p11, p12 end if p12 < p21 then return {} --No overlap elseif p12 == p21 then return {p12} --Overlaps in a point elseif p12 > p21 then if p12 < p22 then return {p21, p12} --Some overlapping segment else return {p21, p22} --Entirity of (p21, p22) is contained in (p11, p12) end else error() end end --[[ Given two number ranges (p11, p12) and (p21, p22), return the second range removed from the first. ]] function numbersDifference(p11: number, p12: number, p21: number, p22: number): {number} --Ensure segments (p11, p12) and (p21, p22) go from small to large --Ensure p11 comes before p12 p11, p12 = minMax(p11, p12) --Ensure p21 comes before p22 p21, p22 = minMax(p21, p22) --Nothing or everything gets deleted local intersection = numbersIntersection(p11, p12, p21, p22) if #intersection == 0 then --No overlap return {p11, p12} --Just return first segment elseif intersection[1] == p11 and intersection[2] == p12 then --Overlap is entire first segment return {} --Nothing left end --Something but not everything gets deleted if p21 <= p11 then -- (p21, p22) starts before (p11, p12) and ends inside --Only 1 case because if p22 were >= p12, everything would get deleted and that's checked previously return {p11, p22, p12}, 1 --First part gets deleted, second part stays elseif p22 >= p12 then -- (p21, p22) starts inside (p11, p12) and ends outside then --Only 1 case because if p21 were <= p11, everything would get deleted and that's checked previously return {p11, p21, p12}, 2 --Second part gets deleted, first part stays then else --Second segment is inside first segment, so cut out middle part return {p11, p21, p22, p12}, 2 --Middle gets deleted, other parts stay end end --[[ Return a non-rotated Part with Size and Position defined by two corners. Optionally set the Size and Position of an existing Part, or use a new Part. ]] function partFromCorners(corner1: Vector3, corner2: Vector3, existingPart: Part?): Part local part = existingPart or Instance.new("Part") local diff = corner2 - corner1 local mid = avg(corner1, corner2) part.Size = Vector3.new(diff.X, diff.Y, diff.Z) part.Position = Vector3.new(mid.X, mid.Y, mid.Z) return part end --[[ Given a non-rotated Part, an Axis and a list of points along that axis (as numbers), return the slices of the Part on the plane perpendicular to the axis and intersecting the points along the axis, as a list of Parts. ]] function slicePart(part: Part, axis: Enum.Axis, points: {number}) local slices = {} local cutAxis = Vector3.FromAxis(axis) local cutPlane = Vector3.new(1, 1, 1) - cutAxis local sCorner1 = corner1(part) local sCorner2 = sCorner1 * cutAxis + corner2(part) * cutPlane --Gets zero'd on the cut axis (so it corner2 on cut axis = corner 1 on cut axis) for i = 1, #points do sCorner2 += points[i] * cutAxis - sCorner1 * cutAxis --Slide corner2 to point[i] on the cut axis table.insert(slices, partFromCorners(sCorner1, sCorner2, part:Clone())) sCorner1 += (sCorner2 - sCorner1) * cutAxis --Slide corner1 to corner2 on the cut axis end sCorner2 = corner2(part) table.insert(slices, partFromCorners(sCorner1, sCorner2, part:Clone())) return slices end --[[ Given two volumes (as Parts), return the intersection of those volumes (as a Part). Input parts must have no rotation. ]] function volumesIntersection(block1: Part, block2: Part): Part? local b1c1, b1c2 = corner1(block1), corner2(block1) local b2c1, b2c2 = corner1(block2), corner2(block2) local xDifference = numbersDifference(b1c1.X, b1c2.X, b2c1.X, b2c2.X) local yDifference = numbersDifference(b1c1.Y, b1c2.Y, b2c1.Y, b2c2.Y) local zDifference = numbersDifference(b1c1.Z, b1c2.Z, b2c1.Z, b2c2.Z) --If any axis is non-intersecting, the intersection is empty if #xDifference ~= 2 and #yDifference ~= 2 and #zDifference ~= 2 then return nil end --If all axes are intersecting, return the 3D intersection local corner1 = Vector3.new(xDifference[1], yDifference[1], zDifference[1]) local corner2 = Vector3.new(xDifference[2], yDifference[2], zDifference[2]) local difference = block1:Clone() partFromCorners(corner1, corner2, difference) return difference end --[[ Given two volumes (as Parts), return the second volume removed from the first volume (as a list of Parts). Input parts must have no rotation. ]] function volumesDifference(block1: Part, block2: Part): {Part} local parts = {} -- Compute the differences on each axis *before* slicing on the axes, -- because we might exit early if one or more axes are non-overlapping local axisDifferences = {} for _, axis in pairs(Enum.Axis:GetEnumItems()) do local A = axis.Name local points, toDelete = numbersDifference(corner1(block1)[A], corner2(block1)[A], corner1(block2)[A], corner2(block2)[A]) axisDifferences[axis] = {points = points, toDelete = toDelete} if #points == 2 then --Or equivalently if toDelte == nil, i.e. block2 and block1 do not intersect. return { block1:Clone() } end end -- Slice along axes after verifying that none are non-interesecting for _, axis in pairs(Enum.Axis:GetEnumItems()) do local A = axis.Name local differences = axisDifferences[axis] local points: {number}, toDelete = differences.points, differences.toDelete print(points) --Slices are to be made between each point, but slicePart expects a list of points to slice AT, not two points to slice BETWEEN. --Fix this by removing first and last points. table.remove(points, #points) table.remove(points, 1) --Skip if (block1 - block2) == block1, i.e. block2 is completely outside block 1 if #points == 0 then continue end --Slice block1 according to where it intersects block2 local slices = slicePart(block1, axis, points) --Return all the slices except the ones that are inside block2 for i = 1, #slices do if i == toDelete then continue end table.insert(parts, slices[i]) end end return parts end First of all, I’m assuming the blocks will always be axis-aligned (i.e. not rotated).
What you want to find is A - B, where A is the grey block and B is the red one. As you’ve shown it, B fits nicely inside A, but that might not always be the case. We might as well solve the problem in a more general case where only some of B overlaps A, or even where they’re not even touching. In that case, what you want to do is remove from A the volume that is shared between A and B, which can be written as A ∩ B (a symbol from set theory meaning “intersection”). So the first step is to find A ∩ B.
It’s helpful to solve it first in 1D and then extend the solution to 3D, because 1D is much simpler and easier to think about. In 1D there are a few cases that must be handled differently.
Case 1 (no overlap)

Case 2 (overlap in a point)

Case 3 (overlap in a segment)

Case 4 (A completely inside B)

You might wonder why there are not cases where A is to the left of B. That’s because ∩ is completely symmetric, i.e. A ∩ B = B ∩ A. For example this case…
is the same as case 1 except the colors and labels are switched, but because of that property of ∩ it’s the same as case 1.
Since we’re working in 1D the points are just numbers on the number line, so let’s create a function called numbersIntersection that finds A ∩ B where A=(p11, p12) and B=(p21, p22):
function numbersIntersection(p11, p12, p21, p22) -- ... something something -- return {point1 of A ∩ B, point 2 of A ∩ B} end The function will return a table containing 0, 1 or 2 points (which are numbers because 1D) that make up A ∩ B.
function minMax(v1, v2) return math.min(v1, v2), math.max(v1, v2) end function numbersIntersection(p11, p12, p21, p22) --Ensure segments (p11, p12) and (p21, p22) go from small to large --Ensure p11 comes before p12 p11, p12 = minMax(p11, p12) --Ensure p21 comes before p22 p21, p22 = minMax(p21, p22) --Ensure (p11, p12) starts before (p21, p22) if p11 > p21 then --... by swapping if that's not the case p11, p12, p21, p22 = p21, p22, p11, p12 end -- ... return something end -
function numbersIntersection(p11, p12, p21, p22) --Ensure segments (p11, p12) and (p21, p22) go from small to large --Ensure p11 comes before p12 p11, p12 = minMax(p11, p12) --Ensure p21 comes before p22 p21, p22 = minMax(p21, p22) --Ensure (p11, p12) starts before (p21, p22) if p11 > p21 then --... by swapping if that's not the case p11, p12, p21, p22 = p21, p22, p11, p12 end if p12 < p21 then --End of segment 1 comes before start of segment 2 return {} --No overlap elseif p12 == p21 then --End of segment 1 is same as start of segment 2 return {p12} --Overlaps in a point elseif p12 > p21 then --End of segment 1 inside segment 2 if p12 < p22 then --Start of segment 1 before start of segment 2 return {p21, p12} --Some overlapping segment else --Start of segment 1 inside segment 2 return {p21, p22} --Entirity of (p21, p22) is contained in (p11, p12) end end end The intersection of two 1D segments was (at most) a 1D segment made up of two numbers.
Likewise, the intersection of two 2D rectangles is (at most) a 2D rectangle made up of 2 1D segments at right angles (because we know they’re not rotated), or 4 numbers (start and end on X axis, start and end on Y axis). Once again because we know the rectangles are axis aligned we don’t actually need all 4 points, we can figure out the rest if we just have 2 opposing corners.
The 2D solution illustrated:
The intersection is the rectangle made up of the two segments (projected onto the X and Y axes in this illustration). We can find those segments as the intersections of the vertical and horizontal parts of the rectangles.
The intersection of two 3D cuboids is at most a 3D cuboid made up of 3 1D segments at right angles, or 6 numbers. Once again we only need the two opposing corners. We can find those segments as the intersections of the vertical, horizontal and depth-wise parts of the cuboids.
function corner1(part) return part.Position - part.Size / 2 end function corner2(part) return part.Position + part.Size / 2 end function minMax(v1, v2) return math.min(v1, v2), math.max(v1, v2) end function avg(v1, v2) return (v1 + v2) / 2 end function numbersIntersection(p11, p12, p21, p22) --Ensure segments (p11, p12) and (p21, p22) go from small to large --Ensure p11 comes before p12 p11, p12 = minMax(p11, p12) --Ensure p21 comes before p22 p21, p22 = minMax(p21, p22) --Ensure (p11, p12) starts before (p21, p22) if p11 > p21 then --... by swapping if that's not the case p11, p12, p21, p22 = p21, p22, p11, p12 end if p12 < p21 then return {} --No overlap elseif p12 == p21 then return {p12} --Overlaps in a point elseif p12 > p21 then if p12 < p22 then return {p21, p12} --Some overlapping segment else return {p21, p22} --Entirity of (p21, p22) is contained in (p11, p12) end end end function partFromCorners(corner1, corner2, part) local part = part or Instance.new("Part") local diff = corner2 - corner1 local mid = avg(corner1, corner2) part.Size = Vector3.new(diff.X, diff.Y, diff.Z) part.Position = Vector3.new(mid.X, mid.Y, mid.Z) return part end function volumesIntersection(block1: Part, block2: Part) local b1c1, b1c2 = corner1(block1), corner2(block1) local b2c1, b2c2 = corner1(block2), corner2(block2) local xIntersection = numbersIntersection(b1c1.X, b1c2.X, b2c1.X, b2c2.X) local yIntersection = numbersIntersection(b1c1.Y, b1c2.Y, b2c1.Y, b2c2.Y) local zIntersection = numbersIntersection(b1c1.Z, b1c2.Z, b2c1.Z, b2c2.Z) if #xIntersection == 2 and #yIntersection == 2 and #zIntersection == 2 then local corner1 = Vector3.new(xIntersection[1], yIntersection[1], zIntersection[1]) local corner2 = Vector3.new(xIntersection[2], yIntersection[2], zIntersection[2]) local intersection= block1:Clone() partFromCorners(corner1, corner2, intersection) return intersection end end Testing it I get this:

local a, b = game.Workspace.A, game.Workspace.B local u = volumesIntersection(a, b) u.Name = "A∩B" u.Color = Color3.fromRGB(255, 0, 255) u.Parent = game.Workspace Now that we can calculate A ∩ B, it’s not so difficult to calculate A - B.
In the case where there’s no overlap, the result of the operation is to just leave A be, because B doesn’t “take a bite” out of it.
Cases 1 and 2: A is cut into 2 segments, where either the first or last one is deleted.
Case 3: A gets cut into 3 segments where the middle one is deleted.

Case 4: A is entirely eaten by B so there are 0 segments left:

Let’s look at case 1 in 2D:
If we were to think of how we’d apply the 1D solution to figure out the 4 - 1 = 3 rectangles to create in 2D, we’d first cut A into 2 rectangles along one axis, leave one of them be and then cut the other into 2 rectangles on the other axis. This gives us 3 different parts of which 1 must be deleted.
To create the parts we need in 3D we use the same logic: cut first on one axis, then another axis, and then the final axis:

In this image I first cut the part on the Y axis, then one of the horizontal axes and then the other. So first I turned the blue box that was the entire part into 2 boxes, coloring the top one green. Then I cut the green in two, coloring one of the parts yellow. Then I cut the yellow box in 2 and deleted one of the yellow parts.
A similar logic applies for case 3 where the middle must be removed. Notice that the 3 cases can be mixed and matched between the 3 axes, e.g.:
Cutting a hole in a part:

3 blue segments, middle gets deleted. 3 green segments, middle gets deleted. 0 yellow segments because B completely ate A on that axis.
Cutting a notch into a part:
B only ate one end of A on that axis:

For the same reasons as in part 1, we fix the order of the parameters so they’re always right. Except we don’t swap which segment is which, because A - B ~= B - A.
function numbersSub(p11, p12, p21, p22) --Ensure segments (p11, p12) and (p21, p22) go from small to large --Ensure p11 comes before p12 p11, p12 = minMax(p11, p12) --Ensure p21 comes before p22 p21, p22 = minMax(p21, p22) ... end function numbersSub(p11, p12, p21, p22) --Ensure segments (p11, p12) and (p21, p22) go from small to large --Ensure p11 comes before p12 p11, p12 = minMax(p11, p12) --Ensure p21 comes before p22 p21, p22 = minMax(p21, p22) --Nothing or everything gets deleted local intersection= numbersIntersection(p11, p12, p21, p22) if #intersection== 0 then --No overlap return {p11, p12} --Just return first segment elseif intersection[1] == p11 and intersection[2] == p12 then --Overlap is entire first segment return {} --Nothing left end ... function numbersSub(p11, p12, p21, p22) --Ensure segments (p11, p12) and (p21, p22) go from small to large --Ensure p11 comes before p12 p11, p12 = minMax(p11, p12) --Ensure p21 comes before p22 p21, p22 = minMax(p21, p22) --Nothing or everything gets deleted local intersection= numbersIntersection(p11, p12, p21, p22) if #intersection== 0 then --No overlap return {p11, p12} --Just return first segment elseif intersection[1] == p11 and intersection[2] == p12 then --Overlap is entire first segment return {} --Nothing left end --Something but not everything gets deleted if p21 <= p11 then -- (p21, p22) starts before (p11, p12) and ends inside --Only 1 case because if p22 were >= p12, everything would get deleted and that's checked previously return {p11, p22, p12}, 1 --First part gets deleted, second part stays elseif p22 >= p12 then -- (p21, p22) starts inside (p11, p12) and ends outside then --Only 1 case because if p21 were <= p11, everything would get deleted and that's checked previously return {p11, p21, p12}, 2 --Second part gets deleted, first part stays then else --Second segment is inside first segment, so cut out middle part return {p11, p21, p22, p12}, 2 --Middle gets deleted, other parts stay end end function slicePart(part: Part, axis: Enum.Axis, points) local slices = {} local cutAxis = Vector3.FromAxis(axis) local cutPlane = Vector3.new(1, 1, 1) - cutAxis local sCorner1 = corner1(part) local sCorner2 = sCorner1 * cutAxis + corner2(part) * cutPlane --Gets zero'd on the cut axis (so it corner2 on cut axis = corner 1 on cut axis) for i = 1, #points do sCorner2 += points[i] * cutAxis - sCorner1 * cutAxis --Slide corner2 to point[i] on the cut axis table.insert(slices, partFromCorners(sCorner1, sCorner2, part:Clone())) sCorner1 += (sCorner2 - sCorner1) * cutAxis --Slide corner1 to corner2 on the cut axis end sCorner2 = corner2(part) table.insert(slices, partFromCorners(sCorner1, sCorner2, part:Clone())) return slices end It keeps track of two corners to create the slices at. They both start off at one end of the part, but still opposite each other (i.e. as opposite corners on one face of the part). The slice is then “walked along” the axis, first moving sCorner2 to the next point, then creating the slice, then moving sCorner1 up to sCorner2 along the cut axis, kind of like a worm or something.
function volumesSub(block1: Part, block2: Part) local b1c1, b1c2 = corner1(block1), corner2(block1) local b2c1, b2c2 = corner1(block2), corner2(block2) local parts = {} for _, axis in pairs(Enum.Axis:GetEnumItems()) do local A = axis.Name local points, toDelete = numbersSub(b1c1[A], b1c2[A], b2c1[A], b2c2[A]) --Slices are to be made between each point, but slicePart expects a list of points to slice AT, not two points to slice BETWEEN. --Fix this by removing first and last points. table.remove(points, #points) table.remove(points, 1) --Skip if (block1 - block2) == block1, i.e. block2 is completely outside block 1 if #points == 0 then continue end --Slice block1 according to where it intersects block2 local slices = slicePart(block1, axis, points) --Return all the slices except the ones that are inside block2 for i = 1, #slices do if i == toDelete then continue end table.insert(parts, slices[i]) end end return parts end local a, b, c, d = game.Workspace.A, game.Workspace.B, game.Workspace.C, game.Workspace.D local u = volumesIntersection(a, b) u.Name = "A∩B" u.Color = Color3.fromRGB(255, 0, 255) u.Parent = game.Workspace local s = volumesSub(c, d) for _, part in pairs(s) do part.Parent = game.Workspace part.Transparency = 0 end 


How would I make it so the s parts wont collide with eachother, without re cutting them?. would be highly appreciated 

Ah, looks like a bug
I’ll take a look when I have time
Yeah, there was a bug where it kept reusing the original block to slice each axis, when it should have been using the results of slicing from the previous axis.
function corner1(part) return part.Position - part.Size / 2 end function corner2(part) return part.Position + part.Size / 2 end function minMax(v1, v2) return math.min(v1, v2), math.max(v1, v2) end function avg(v1, v2) return (v1 + v2) / 2 end --[[ Given two number ranges (p11, p12) and (p21, p22), return the intersection of those ranges. ]] function numbersIntersection(p11: number, p12: number, p21: number, p22: number): {number} --Ensure segments (p11, p12) and (p21, p22) go from small to large --Ensure p11 comes before p12 p11, p12 = minMax(p11, p12) --Ensure p21 comes before p22 p21, p22 = minMax(p21, p22) --Ensure (p11, p12) starts before (p21, p22) if p11 > p21 then --... by swapping if that's not the case p11, p12, p21, p22 = p21, p22, p11, p12 end if p12 < p21 then return {} --No overlap elseif p12 == p21 then return {p12} --Overlaps in a point elseif p12 > p21 then if p12 < p22 then return {p21, p12} --Some overlapping segment else return {p21, p22} --Entirity of (p21, p22) is contained in (p11, p12) end else error() end end --[[ Given two number ranges (p11, p12) and (p21, p22), return the second range removed from the first. ]] function numbersDifference(p11: number, p12: number, p21: number, p22: number): {number} --Ensure segments (p11, p12) and (p21, p22) go from small to large --Ensure p11 comes before p12 p11, p12 = minMax(p11, p12) --Ensure p21 comes before p22 p21, p22 = minMax(p21, p22) --Nothing or everything gets deleted local intersection = numbersIntersection(p11, p12, p21, p22) if #intersection == 0 then --No overlap return {p11, p12} --Just return first segment elseif intersection[1] == p11 and intersection[2] == p12 then --Overlap is entire first segment return {} --Nothing left end --Something but not everything gets deleted if p21 <= p11 then -- (p21, p22) starts before (p11, p12) and ends inside --Only 1 case because if p22 were >= p12, everything would get deleted and that's checked previously return {p11, p22, p12}, 1 --First part gets deleted, second part stays elseif p22 >= p12 then -- (p21, p22) starts inside (p11, p12) and ends outside then --Only 1 case because if p21 were <= p11, everything would get deleted and that's checked previously return {p11, p21, p12}, 2 --Second part gets deleted, first part stays then else --Second segment is inside first segment, so cut out middle part return {p11, p21, p22, p12}, 2 --Middle gets deleted, other parts stay end end --[[ Return a non-rotated Part with Size and Position defined by two corners. Optionally set the Size and Position of an existing Part, or use a new Part. ]] function partFromCorners(corner1: Vector3, corner2: Vector3, existingPart: Part?): Part local part = existingPart or Instance.new("Part") local diff = corner2 - corner1 local mid = avg(corner1, corner2) part.Size = Vector3.new(diff.X, diff.Y, diff.Z) part.Position = Vector3.new(mid.X, mid.Y, mid.Z) return part end --[[ Given a non-rotated Part, an Axis and a list of points along that axis (as numbers), return the slices of the Part on the plane perpendicular to the axis and intersecting the points along the axis, as a list of Parts. ]] function slicePart(part: Part, axis: Enum.Axis, points: {number}) local slices = {} local cutAxis = Vector3.FromAxis(axis) local cutPlane = Vector3.new(1, 1, 1) - cutAxis local sCorner1 = corner1(part) local sCorner2 = sCorner1 * cutAxis + corner2(part) * cutPlane --Gets zero'd on the cut axis (so it corner2 on cut axis = corner 1 on cut axis) for i = 1, #points do sCorner2 += points[i] * cutAxis - sCorner1 * cutAxis --Slide corner2 to point[i] on the cut axis table.insert(slices, partFromCorners(sCorner1, sCorner2, part:Clone())) sCorner1 += (sCorner2 - sCorner1) * cutAxis --Slide corner1 to corner2 on the cut axis end sCorner2 = corner2(part) table.insert(slices, partFromCorners(sCorner1, sCorner2, part:Clone())) return slices end --[[ Given two volumes (as Parts), return the intersection of those volumes (as a Part). Input parts must have no rotation. ]] function volumesIntersection(block1: Part, block2: Part): Part? local b1c1, b1c2 = corner1(block1), corner2(block1) local b2c1, b2c2 = corner1(block2), corner2(block2) local xDifference = numbersDifference(b1c1.X, b1c2.X, b2c1.X, b2c2.X) local yDifference = numbersDifference(b1c1.Y, b1c2.Y, b2c1.Y, b2c2.Y) local zDifference = numbersDifference(b1c1.Z, b1c2.Z, b2c1.Z, b2c2.Z) --If any axis is non-intersecting, the intersection is empty if #xDifference ~= 2 and #yDifference ~= 2 and #zDifference ~= 2 then return nil end --If all axes are intersecting, return the 3D intersection local corner1 = Vector3.new(xDifference[1], yDifference[1], zDifference[1]) local corner2 = Vector3.new(xDifference[2], yDifference[2], zDifference[2]) local difference = block1:Clone() partFromCorners(corner1, corner2, difference) return difference end --[[ Given two volumes (as Parts), return the second volume removed from the first volume (as a list of Parts). Input parts must have no rotation. ]] function volumesDifference(block1: Part, block2: Part): {Part} -- Compute the differences on each axis *before* slicing on the axes, -- because we might exit early if one or more axes are non-overlapping local axisDifferences = {} for _, axis in pairs(Enum.Axis:GetEnumItems()) do local A = axis.Name local points, toDelete = numbersDifference(corner1(block1)[A], corner2(block1)[A], corner1(block2)[A], corner2(block2)[A]) if #points == 2 then --Or equivalently if toDelte == nil, i.e. block2 and block1 do not intersect, so (block1 - block2) = block1 return { block1:Clone() } end axisDifferences[axis] = {points = points, toDelete = toDelete} end -- Slice along axes after verifying that none are non-interesecting local parts = { } local partToSlice = block1 for _, axis in pairs(Enum.Axis:GetEnumItems()) do local A = axis.Name local differences = axisDifferences[axis] local points: {number}, toDelete: number = differences.points, differences.toDelete --Slices are to be made between each point, but slicePart expects a list of points to slice AT, not two points to slice BETWEEN. --Fix this by removing first and last points. table.remove(points, #points) table.remove(points, 1) --Skip if (block1 - block2) == block1, i.e. block2 is completely outside block 1 if #points == 0 then continue end --Slice block1 according to where it intersects block2 local slices = slicePart(partToSlice, axis, points) --Return all the slices except the ones that are inside block2 for i = 1, #slices do if i == toDelete then partToSlice = slices[i] else table.insert(parts, slices[i]) end end end return parts end return { volumesIntersection = volumesIntersection, volumesDifference = volumesDifference } Hello, why doesn’t the volumeintersect work? Could you further explain how it works?