The following uses the vertices of the top and bottom edges, where u
is approximately one of {0, 1}
. It is only approximate because ParametricPlot3D
uses open sampling. The advantage to using the actually discrete edges of the graphics is that the lids "fit tightly."
ParametricPlot3D[{(1 + Cos[(8 t)]) Cos[t], (1 + Cos[8 t]) Sin[t], u},
{t, 0, 2 Pi}, {u, 0, 1},
PlotPoints -> {120, 5},
Mesh -> None, PlotStyle -> Opacity[0.9],
BoundaryStyle -> Opacity[0.9]] /.
GraphicsComplex[pts_, g_, opts_] :>
GraphicsComplex[
pts, (* array of coordinates of points in the graphics *)
g /. Line[idcs_] :> { (* points are specified by their indices in the coordinate array pts *)
EdgeForm[],
Polygon[
SplitBy[
Select[
idcs,
Min@Abs[pts[[#, 3]] - {0, 1}] < 1*^-6 &], (* select indices at the boundary edges {0, 1} *)
Nearest[{0, 1}, pts[[#, 3]]] &], (* split the indices according to bottom, top {0, 1} *)
VertexNormals -> None] (* the polygons are virtually flat and should not inherit the VertexNormals
assigned to their points on the original surface (the sides) *)
},
opts]
Alternatively, one can construct a cylinder given a base polygon.
base = First@Cases[
ParametricPlot[{(1 + Cos[(8 t)]) Cos[t], (1 + Cos[8 t]) Sin[t]}, {t, 0, 2 Pi}],
Line[p_] :> p, Infinity];
RegionProduct[Polygon[base], Line[{{0.}, {1.}}]]
Or just construct the base directly with Table
:
base = Most@
Table[{(1 + Cos[(8 t)]) Cos[t], (1 + Cos[8 t]) Sin[t]},
{t, 0., 2 Pi, 2 Pi/8/100}]; (* 100 yields a sufficient sampling density *)