How to combine several surfaces into a solid and compute its volume?

plot1 is taken from the BSplineSurface document. I add two Disk plot2 and plot3 to fill the surface in order to build a real solid. But BoundaryDiscretizeGraphics won't work. I don't know how to fix it.

pts = {{{0.5, 0, -0.5}, {0, 0, -0.5}, {0, 1, -0.5}, {0.5,
1, -0.5}, {1, 1, -0.5}, {1, 0, -0.5}, {0.5, 0, -0.5}},
{{0.5, 0, 0.7}, {0, 0, 0.7}, {0, 1, 0.7}, {0.5, 1, 0.7}, {1, 1,
0.7}, {1, 0, 0.7}, {0.5, 0, 0.7}},
{{0.5, 0, 0.9}, {0, 0, 0.9}, {0, 1, 1.5}, {0.5, 1, 1.5}, {1, 1,
1.5}, {1, 0, 0.9}, {0.5, 0, 0.9}},
{{0.5, -0.1, 1}, {0, -0.1, 1}, {0, 0.5, 2}, {0.5, 0.5, 2}, {1,
0.5, 2}, {1, -0.1, 1}, {0.5, -0.1, 1}},
{{0.5, -0.3, 1}, {0, -0.3, 1}, {0, -0.3, 2}, {0.5, -0.3,
2}, {1, -0.3, 2}, {1, -0.3, 1}, {0.5, -0.3, 1}},
{{0.5, -1.5, 1}, {0, -1.5, 1}, {0, -1.5, 2}, {0.5, -1.5,
2}, {1, -1.5, 2}, {1, -1.5, 1}, {0.5, -1.5, 1}}}; w = {{1, .5, .5,
1, .5, .5, 1}, {1, .5, .5, 1, .5, .5, 1}, {1, .5, .5, 1, .5, .5,
1}, {1, .5, .5, 1, .5, .5, 1}, {1, .5, .5, 1, .5, .5,
1}, {1, .5, .5, 1, .5, .5, 1}};
uk = {0, 0, 0, 1/4, 1/2, 3/4, 1, 1, 1};
vk = {0, 0, 0, 1/4, 1/2, 1/2, 3/4, 1, 1, 1};
plot1 = Graphics3D[{
FaceForm[Yellow, Blue],
BSplineSurface[pts, SplineKnots -> {uk, vk}, SplineDegree -> 2,
SplineWeights -> w, SplineClosed -> {False, True}]},
ViewPoint -> {Right, Front}, Boxed -> False];
plot2 = Graphics3D[
BSplineSurface[{{{1., 0.5, -0.5}, {1., 1., -0.5}, {0.5,
1., -0.5}}, {{1., 0., -0.5}, {0.5, 0.5, -0.5}, {0.,
1., -0.5}}, {{0.5, 0., -0.5}, {0., 0., -0.5}, {0.,
0.5, -0.5}}},
SplineKnots -> {{0, 0, 0, 1, 1, 2}, {0, 0, 0, 1, 1, 2}},
SplineWeights -> {{1, 1/Sqrt[2], 1}, {1/Sqrt[2], 1,
1/Sqrt[2]}, {1, 1/Sqrt[2], 1}}]];

plot3 = Graphics3D[
BSplineSurface[{{{1., -1.5, 1.5}, {1., -1.5, 2.}, {0.5, -1.5,
2.}}, {{1., -1.5, 1.}, {0.5, -1.5, 1.5}, {0., -1.5,
2.}}, {{0.5, -1.5, 1.}, {0., -1.5, 1.}, {0., -1.5, 1.5}}},
SplineKnots -> {{0, 0, 0, 1, 1, 2}, {0, 0, 0, 1, 1, 2}},
SplineWeights -> {{1, 1/Sqrt[2], 1}, {1/Sqrt[2], 1,
1/Sqrt[2]}, {1, 1/Sqrt[2], 1}}]];

plot=Show[plot1, plot2, plot3]
DiscretizeGraphics[plot]
BoundaryDiscretizeGraphics[plot]

• The most straightforward approach I can see would be to discretize the bsplinesurface, export it out to 3D software, close the holes there, then re-import it back in. Mathematica doesn't like this surface because it's not closed and adding those disks won't close it. Commented Mar 13, 2022 at 17:49

This is a little tricky, and I'm not sure why BoundaryMesh didn't work in the following. (We have to resort to BoundaryMeshRegion instead.) But here's what we do:

1. Discretize the spline with DiscretizeGraphics, which creates a Mesh

2. Use RepairMesh to make the original a closed surface (we don't need the disk caps anymore)

3. Deconstruct the mesh into coordinates and cells via MeshCoordinates and MeshCells respectively, then build up a BoundaryMeshRegion "manually" from these (here's where we'd expect BoundaryMesh to just convert it for us, I think, but it can't for some reason).

4. Take the Volume!

surface1 =
BSplineSurface[pts, SplineKnots -> {uk, vk}, SplineDegree -> 2,
SplineWeights -> w, SplineClosed -> {False, True}];

mesh = RepairMesh[DiscretizeGraphics[surface1], {"HoleEdges"}];

filledMesh =
BoundaryMeshRegion[MeshCoordinates[mesh], MeshCells[mesh, 2]];

(* Check that this is a solid, not a surface: *)
RegionDimension[filledMesh] == 3 (* True *)

Volume[filledMesh] (* 2.994278865 *)

You can feed options to DiscretizeGraphics to improve accuracy.

To get some trust that this is right, here's filledMesh:

Graphics3D[{Opacity[0.5], filledMesh}]

Note that FindMeshDefects[filledMesh] doesn't highlight any defects, so there's nothing obviously wrong with it as a mesh.

(However, FindMeshDefects[DiscretizeSurface[surface1]], before repairing, does find hole edges. In fact, that's how I knew to use RepairMesh like this!)

And as a sanity check for the volume, let's find the volume of the bounding box manually and make sure it leads to about what we'd expect.

boundingbox = MinMax /@ Transpose @ MeshCoordinates[mesh]
bboxdims = -(Subtract @@@ boundingbox) (* {1., 2.499999314, 2.499999314} *)
Times @@ bboxdims (* 6.249996571 *)

Cool—so to approximate the spline region, visually, judge the diameter of the tube to be about $$2/5$$ the long dimension (and therefore approximately $$1$$). We'd expect about a box of proportions $$1 , (3/5) , (3/5)$$ relative to the bounding box to be missing (the "empty space" that the tube wraps around), which takes away a volume of $$1 \times 1.5 \times 1.5 = 2.25$$ from the total bounding box region of $$6.25$$. The remaining space can itself be thought of as the bounding box of a cylinder (very roughly), which means we reduce by a factor of $$\pi/4$$ ($$V_\text{cylinder} = \pi hr^2 = \frac{\pi}{4}hd^2 = \frac{\pi}{4}V_\text{bounding prism}$$). $$\frac{\pi}{4}(6.25 - 2.25) = \pi = 3.14...$$; and then you remember, hey, we probably lose a bit of extra volume when we take the corner bend into account, so a volume of about 3 is reasonable. :)