5
$\begingroup$

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]

enter image description here

$\endgroup$
1
  • $\begingroup$ 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. $\endgroup$
    – flinty
    Mar 13, 2022 at 17:49

1 Answer 1

8
$\begingroup$

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}]

The output of the above, which is the original spline surface but "closed up".

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. :)

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.