Function Repository Resource:

CylindricalFlange3D

Source Notebook

Represent a cylinder-like 3D object with protrusions

Contributed by: Jaroslav Kysela

ResourceFunction["CylindricalFlange3D"][]

represents a cylinder with one protrusion.

ResourceFunction["CylindricalFlange3D"][type]

represents a graphics object of type type with one protrusion.

ResourceFunction["CylindricalFlange3D"][type,frspec]

represents an object with front protrusions specified by frspec.

ResourceFunction["CylindricalFlange3D"][type,frspec,bkspec]

specifies front and back protrusions.

ResourceFunction["CylindricalFlange3D"][type,frspec,bkspec,circspec]

specifies front and back protrusions, as well as protrusions along the circumference.

Details and Options

ResourceFunction["CylindricalFlange3D"] can be used in Graphics3D.
ResourceFunction["CylindricalFlange3D"] in general returns a (possibly large and nested) list of graphics primitives such as Cylinder or BSplineSurface that together constitute a flange.
ResourceFunction["CylindricalFlange3D"] is intended as a convenience function that allows for easy creation of composed cylinder-like graphics objects with textual labels. Its purpose is purely visual, no computation with the output (such as RegionMeasure) is supported.
Graphics rendering is affected by directives such as FaceForm, Opacity, EdgeForm, and color.
The first argument specifies the shape and size of the main graphics object, the other arguments specify the front and back protrusions and the protrusions along the circumference, respectively.
The first argument, type, can take any of the following forms:
Automaticdefault shape is set to "Cylinder"
shapenamestring specifying the shape, see below
{shapename,length}specify the length of the body (default is 0.4)
{shapename,length,rad}specify the (outer) radius (default is 1)
{shapename,length,rad,pars}specify additional parameters such as rounding radii (if any exist)
Parameter shapename can take on the following forms:
"Annulus3D"object given by ResourceFunction["Annulus3D"]
"Cylinder"object given by Cylinder
"Empty"no object created
"RoundedAnnulus3D"object given by ResourceFunction["RoundedAnnulus3D"]
"RoundedCylinder"object given by ResourceFunction["RoundedCylinder"]
fun[len,rad,pars]object generated by a pure function
For named shapes, the input parameters are checked for whether they lie in the allowed range. When a pure function is supplied instead, no parameter preprocessing is performed.
The second, third, and fourth arguments can be of the following forms:
numinteger number num of evenly distributed protrusions on a given face
posspeclist of protrusions' positions specified in the plane of the face (for front and back face) or along the surface of the circumference
{posspec,shapespec}specify both the positions as well as the shape of the protrusions, see below
The position specification posspec for protrusions can have the following forms:
numnon-negative number of protrusions, evenly distributed on the face
{{rad1,th1},{rad2,th2},}list of polar coordinates for individual protrusions
{{rad1,th1,em1},{rad2,th2,em2},}together with the position also specify for each protrusion how much it should protrude from the surface
The meaning of the polar coordinates is slightly different for front and back faces on the one hand, and for the circumference on the other hand. For the former, the first coordinate denotes a relative radial position of the protrusion on the face, while for the latter, the first coordinate specifies the relative position with respect to the center plane cutting through the main body. The meaning of the second and third coordinates is the same for all three faces. Namely, the second coordinate defines the angular position along the face and the third coordinate characterizes how much the protrusion sinks into the surface or protrudes from it.
The shape specification shapespec for protrusions follows exactly the shape specification for the main body. The allowed shapes are, however, slightly different:
"Annulus3D"object given by ResourceFunction["Annulus3D"]
"Cuboid"object given by Cuboid
"Cylinder"object given by Cylinder
"RoundedAnnulus3D"object given by ResourceFunction["RoundedAnnulus3D"]
"RoundedCuboid"object given by ResourceFunction["RoundedCuboid"]
"RoundedCylinder"object given by ResourceFunction["RoundedCylinder"]
fun[len,rad,pars]object generated by a pure function
The following full specifications for each shape are allowed:
{"Cylinder",len,rad}cylinder of length len and radius rad
{"Empty",len,rad}empty cylinder-like object of given length and radius
{"Annulus3D",len,rad,inrad}3D annulus with inner radius inrad
{"Cuboid",z,hx,hy}cuboid with length z, half-width hx, and half-depth hy
{"RoundedCylinder",len,rad,rrad}rounded cylinder with rounding radius rrad
{"RoundedAnnulus3D",len,rad,inrad,rrad}rounded 3D annulus with inner radius inrad and rounding radius rrad
{"RoundedCuboid",z,hx,hy,rrad}rounded cuboid with rounding radius rrad
When only a partial shape specification is given, default values are used instead. The default value for len is 0.4 for the main body and 0.2 for protrusions; for rad it is 1 for body and 0.1 for protrusions. Additional parameters are dynamically calculated from the values of these two parameters (if not supplied explicitly). The inner radius inrad and rounding radius rrad are set to 0.5rad and 0.01rad, respectively. For "Cuboid" and "RoundedCuboid" z is set to len, while hx and hy are set to rad.
The following options can be given:
"Label"Nonetextual label on the main object
PlotPoints20resolution of the cylindrical mesh underlying the label
RasterSize200raster size of each label
"RotateProtrusions"Automaticwhether to rotate the front and back protrusions
Textual labels specified by "Label" are represented as rasterized images wrapped in Texture that spans a mesh created by RevolutionPlot3D.
The "Label" setting can have the following forms:
None | Automaticno label
"SomeText…"text of the label
Style[stylespec]text wrapped in Style
{textspec,scale}specify also a scale (default scale is 1)
{textspec,scale,orientation}specify orientation (can be any real number, default is 0)
The "RotateProtrusions" setting can have the following forms:
True|Falsespecify whether the protrusions are rotated for both the front and the back face
Automaticequivalent to False for types "Cylinder", "RoundedCylinder", "Annulus3D", and "RoundedAnnulus3D", and to True otherwise (default)
{frspec,bkspec}specify individually for front and back face

Examples

Basic Examples (5) 

Flange with five "rivets":

In[1]:=
Graphics3D[ResourceFunction["CylindricalFlange3D"]["Annulus3D", 5]]
Out[1]=

Wheel on a shaft:

In[2]:=
Graphics3D[{Orange, ResourceFunction["CylindricalFlange3D"][{"Cylinder", 0.2, 0.7}, 1, 1]}]
Out[2]=

Gear:

In[3]:=
Graphics3D[{ResourceFunction["CylindricalFlange3D"]["Annulus3D", 0, 0, {15, {"Cuboid", 0.5, 0.15, 0.15}}]}]
Out[3]=

Collar with a label:

In[4]:=
Graphics3D[{ResourceFunction["CylindricalFlange3D"]["Annulus3D", 0, "Label" -> "Component No. 5"]}]
Out[4]=

Customize the shapes:

In[5]:=
Graphics3D[{ResourceFunction["CylindricalFlange3D"][
   Ball[{0, 0, 0}, #2] &, 0, 0, {15, {Cone[{{0, 0, 0}, {0, 0, #1}}, #2] &, 0.8, 0.2, 0.15}}]}]
Out[5]=

Scope (20) 

Main body (6) 

By default, create a cylinder with one protrusion:

In[6]:=
Graphics3D[ResourceFunction["CylindricalFlange3D"][]]
Out[6]=

Specify the shape of the body:

In[7]:=
Graphics3D[ResourceFunction["CylindricalFlange3D"][#], PlotLabel -> #] & /@ {"Cylinder", "RoundedCylinder", "Annulus3D", "RoundedAnnulus3D"}
Out[7]=

Supply custom-made pure function to define the shape:

In[8]:=
Graphics3D[
 ResourceFunction["CylindricalFlange3D"][
  Cone[{{0, 0, 0}, {0, 0, #1}}, #2] &]]
Out[8]=

"Turn off" the body and keep only the protrusions:

In[9]:=
Graphics3D[ResourceFunction["CylindricalFlange3D"]["Empty", 1, 1, 20]]
Out[9]=

Specify the length and the radius of the body:

In[10]:=
{len, rad} = {3, 1};
Graphics3D[
 ResourceFunction["CylindricalFlange3D"][{"Cylinder", len, rad}]]
Out[11]=

Specify additional parameters such as the rounding radius:

In[12]:=
Graphics3D[
 ResourceFunction[
  "CylindricalFlange3D"][{"RoundedCylinder", 3, 1, 0.3}]]
Out[12]=

Front and rear protrusions (7) 

Specify the number of front and/or rear protrusions:

In[13]:=
Graphics3D[{Opacity[0.5], ResourceFunction["CylindricalFlange3D"][Automatic, 7, 3]}]
Out[13]=

Instead of the number, specify the relative polar coordinates in the plane of the base:

In[14]:=
{
 Graphics3D[
  ResourceFunction["CylindricalFlange3D"][
   Automatic, {{1, 0}, {0, 0}, {-1, 0}}], PlotLabel -> "Different relative radii"],
 Graphics3D[
  ResourceFunction["CylindricalFlange3D"][
   Automatic, {{1, 0}, {1, \[Pi]/4}, {1, \[Pi]/2}, {1, 3 \[Pi]/4}, {1,
      4 \[Pi]/4}}], PlotLabel -> "Different angles"],
 Graphics3D[
  ResourceFunction["CylindricalFlange3D"][
   Automatic, {{7/20, \[Pi]/8}, {9/20, \[Pi]/4}, {11/20, 3 \[Pi]/8}, {13/20, \[Pi]/2}, {3/4, 5 \[Pi]/8}}], PlotLabel -> "Different radii and angles"]
 }
Out[14]=

Optional third coordinate specifies how the protrusion sinks into the base:

In[15]:=
Graphics3D[
 ResourceFunction[
  "CylindricalFlange3D"][{"Cylinder", 0.1, 1}, {{-1, 0, 0.1}, {-0.5, 0, 0.05}, {0, 0, 0}, {0.5, 0, -0.05}, {1,
     0, -0.1}}], ViewPoint -> Front, ViewProjection -> "Orthographic"]
Out[15]=

Specify the shape of the protrusions:

In[16]:=
Graphics3D[ResourceFunction["CylindricalFlange3D"][Automatic, {5, #}],
    PlotLabel -> #] & /@ {"Cylinder", "RoundedCylinder", "Annulus3D", "RoundedAnnulus3D", "Cuboid", "RoundedCuboid"}
Out[16]=

Custom-made shape supplied as a pure function:

In[17]:=
Graphics3D[
 ResourceFunction["CylindricalFlange3D"][
  Automatic, {5, Cone[{{0, 0, 0}, {0, 0, #1}}, #2] &}]]
Out[17]=

Specify the length, radius, and additional parameters of protrusions the exact same way as for the main body:

In[18]:=
Graphics3D[{ResourceFunction[
   "CylindricalFlange3D"][{"Annulus3D", 0.2}, {12, {"RoundedCylinder", 0.1, 0.1, 0.09}}]}]
Out[18]=

The coordinates and the detailed shape specification can be supplied simultaneously:

In[19]:=
Graphics3D[
 ResourceFunction["CylindricalFlange3D"][
  Automatic, {{{0, 0}, {0.8, \[Pi]/2}, {0.8, 3 \[Pi]/2}}, {"RoundedCylinder", 1, 0.2, 0.05}}]]
Out[19]=

Protrusions along the circumference (7) 

Specify the number of "teeth" along the circumference:

In[20]:=
Graphics3D[{Opacity[0.5], ResourceFunction["CylindricalFlange3D"]["Cylinder", 0, 0, 5]}]
Out[20]=

Specify their positions instead:

In[21]:=
Graphics3D[
 ResourceFunction["CylindricalFlange3D"]["Cylinder", 0, 0, {{0, -\[Pi]/4}, {0, -\[Pi]/2}, {0, -3 \[Pi]/4}}]]
Out[21]=

The first coordinate specifies the relative position along the revolution axis, the second coordinate the azimuthal angle:

In[22]:=
Graphics3D[
 ResourceFunction["CylindricalFlange3D"]["Cylinder", 0, 0, {{-1, \[Pi]/4}, {0, 2 \[Pi]/4}, {1, 3 \[Pi]/4}}], ViewPoint -> Left]
Out[22]=

Optional third coordinate specifies how much the protrusions sink into the main body:

In[23]:=
coords = Table[{0, ang, -0.8 ang/(2 \[Pi])}, {ang, Most@Subdivide[0, 2 \[Pi], 15]}];
Graphics3D[{Opacity[0.5], ResourceFunction["CylindricalFlange3D"][{"Annulus3D", 0.5, 1, 0.8}, 0, 0, {coords, {"Cuboid", 1, 0.05, 0.2}}]}, ViewPoint -> Above]
Out[24]=

Specify the shape of the teeth:

In[25]:=
Graphics3D[
 ResourceFunction["CylindricalFlange3D"][Automatic, 0, 0, {5, "Cuboid"}]]
Out[25]=

The detailed specification of the tooth shape follows that for front and rear protrusions:

In[26]:=
coords1 = Table[{0, ang, -.1}, {ang, Most@Subdivide[0, 2 \[Pi], 15]}];
coords2 = Table[{0, ang, -.55}, {ang, Most@Subdivide[0, 2 \[Pi], 15]}];
g1 = Graphics3D[{ResourceFunction[
     "CylindricalFlange3D"][{"Annulus3D", 0.1, 1}, 0, 0, {coords1, {"Cuboid", 0.5, 0.05, 1}}]}];
g2 = Graphics3D[{ResourceFunction[
     "CylindricalFlange3D"][{"Annulus3D", 0.5, 1, 0.8}, 0, 0, {coords2, {"Cuboid", 0.5, 0.05, 0.2}}]}];
g3 = Graphics3D[{ResourceFunction[
     "CylindricalFlange3D"][{"Annulus3D", 0.5, 1, 0.8}, 0, 0, {coords2, {"Cuboid", 1, 0.05, 0.2}}]}];
{g1, g2, g3}
Out[31]=

The shape can also be specified as a pure function:

In[32]:=
funProtrusion[len_, rad_, pars___] := ResourceFunction["RoundedFrame3D"][{rad, pars[[1]], len}, {pars[[2]],
    pars[[3]]}, RoundingRadius -> pars[[4]]]
Graphics3D[
 ResourceFunction["CylindricalFlange3D"]["Empty", 0, 0, {15, {funProtrusion[##] &, 0.6, 0.3, {0.4, 0.1, 0.1, 0.01}}}]]
In[33]:=

Options (8) 

Label (4) 

Add a textual label to the body:

In[34]:=
Graphics3D[{ResourceFunction["CylindricalFlange3D"]["Annulus3D", 0, "Label" -> "Some descriptive text"]}]
Out[34]=

Add styling:

In[35]:=
Graphics3D[{ResourceFunction["CylindricalFlange3D"]["Annulus3D", 0, 0,
    0, "Label" -> Style[Column[{"Some text", "Second line"}, Alignment -> Center], FontColor -> Blue, FontFamily -> "Arial"]]}, ViewPoint -> Right]
Out[35]=

Modify the size of the label:

In[36]:=
Graphics3D[{ResourceFunction["CylindricalFlange3D"]["Annulus3D", 0, 0,
    0, "Label" -> {"Some text", 0.2}]}, ViewPoint -> Right]
Out[36]=

Rotate the label:

In[37]:=
Graphics3D[{ResourceFunction["CylindricalFlange3D"]["Annulus3D", 0, 0,
    0, "Label" -> {"Some text", 1, -\[Pi]/4}]}, ViewPoint -> Right]
Out[37]=

PlotPoints (1) 

Adjust the resolution of the mesh that underlies the label:

In[38]:=
Graphics3D[{ResourceFunction["CylindricalFlange3D"]["Empty", 0, "Label" -> ToString[#] <> " points", PlotPoints -> #] /. _EdgeForm -> EdgeForm[Black]}] & /@ {2, 20}
Out[38]=

RasterSize (1) 

Decrease the raster size of textual labels for faster rendering:

In[39]:=
Graphics3D[
 ResourceFunction["CylindricalFlange3D"][Automatic, "Label" -> "Some text", RasterSize -> 50], ViewPoint -> Right]
Out[39]=

RotateProtrusions (2) 

By default, the protrusions are rotated based on their angular position:

In[40]:=
Graphics3D[
 ResourceFunction["CylindricalFlange3D"][Automatic, {5, "Cuboid"}]]
Out[40]=

Turn the automatic rotation off:

In[41]:=
Graphics3D[
 ResourceFunction["CylindricalFlange3D"][Automatic, {5, "Cuboid"}, "RotateProtrusions" -> False]]
Out[41]=

Address front and back protrusions separately:

In[42]:=
Graphics3D[{Opacity[0.5], ResourceFunction["CylindricalFlange3D"][
   Automatic, {5, "Cuboid"}, {5, "Cuboid"}, "RotateProtrusions" -> {False, True}]}]
Out[42]=

Applications (4) 

Water tank with rivets:

In[43]:=
ClearAll[specfun]
{len, rad, inrad} = {4, 2.5, 1.1};
specfun[x_, y_ : 0] := {Table[{x, ang, y}, {ang, Most@Subdivide[0, 2 \[Pi], 10]}], {Ball[{0, 0, 0}, #2] &, 0.1, 0.1, 0.09}};
Graphics3D[{Gray,
  ResourceFunction[
   "CylindricalFlange3D"][{"Annulus3D", len, rad, inrad}, specfun[0.85], 0, specfun[0.8]],
  ResourceFunction["CylindricalFlange3D"][{"Empty", len, rad}, 0, specfun[0.85], specfun[-0.8]],
  ResourceFunction["CylindricalFlange3D"][{"Empty", len, rad}, specfun[(inrad + 0.25 (rad - inrad))/rad], 0, specfun[0.8, -(rad - inrad)]],
  ResourceFunction["CylindricalFlange3D"][{"Empty", len, rad}, 0, specfun[(inrad + 0.25 (rad - inrad))/rad], specfun[-0.8, -(rad - inrad)]]
  }, Axes -> True]
Out[46]=

Saturn:

In[47]:=
Graphics3D[{Lighter@Brown, ResourceFunction[
   "CylindricalFlange3D"][{"Annulus3D", 0.01, 1, 0.6}, {1, {Ball[{0, 0, 0}, #2] &, 1, 0.5, 1}}]}]
Out[47]=

Simple model of a pair of gears:

In[48]:=
Graphics3D[{Gray,
  ResourceFunction[
   "CylindricalFlange3D"][{"RoundedCylinder", 0.3, 2}, {7, {"RoundedCylinder", 0.8, 0.2}}, {1, {"RoundedCylinder", 2,
      0.3}}],
  Translate[Rotate[#, \[Pi]/2, {1, 0, 0}] &@
    ResourceFunction[
     "CylindricalFlange3D"][{"RoundedCylinder", 0.3, 2}, {7, {"RoundedCylinder", 0.8, 0.2}}, {1, {"RoundedCylinder", 4, 0.3}}], {0.4, 2, 2}]
  }]
Out[48]=

Turbine:

In[49]:=
leaf = {Prism[{{0, 0, 0.2}, {0, 0, (0.2 + 3 #1)}, {0.6, 0, 0.2}, {0, 0.04, 0.2}, {0, 0.04, 3.2}, {0.6, 0.04, 0.2}}], Cylinder[{{0, 0, 0}, {0, 0, 0.6}}, 0.08]} &;
Graphics3D[{ResourceFunction[
   "CylindricalFlange3D"][{"RoundedCylinder", 0.9, 0.5, 0.4}, 0, {{{0, 0, -0.1}}, {"Cylinder", 1, 0.3}}, {3, {leaf, 2, 0.3}}]}]
Out[50]=

Properties and Relations (2) 

Multiple flanges can be composed to create more complex objects:

In[51]:=
Graphics3D[{Orange, ResourceFunction[
   "CylindricalFlange3D"][{"RoundedCylinder", 1, 2}, {1, {"RoundedAnnulus3D", 0.8, 1.8, 1.4}}, "Label" -> "Part no. 5 with some info", PlotPoints -> 60, RasterSize -> 300], Black, ResourceFunction[
   "CylindricalFlange3D"][{"Empty", 1, 2}, {1, {"Cylinder", 0.5, 1.4}}], Orange, ResourceFunction[
   "CylindricalFlange3D"][{"Empty", 1, 2}, {1, {"RoundedAnnulus3D", 0.8, 1.2, 1.0}}]}]
In[52]:=

Text wrapped around a cylindrical surface:

In[53]:=
Graphics3D[{ResourceFunction["CylindricalFlange3D"]["Empty", 0, "Label" -> {Style[
      Column[{"Long text that wraps around a cylinder, which could be used for something", "Resolution can be adjusted by the fourth parameter, increase it if necessary"}, Alignment -> Center]], 0.5}, RasterSize -> 500]}]
Out[53]=

Possible Issues (3) 

Round surfaces that are internally represented by BSplineSurface may be rendered with edges:

In[54]:=
Graphics3D[ResourceFunction["CylindricalFlange3D"]["RoundedCylinder"]]
Out[54]=

Increase the sampling rate for splines in Graphics3D:

In[55]:=
Graphics3D[ResourceFunction["CylindricalFlange3D"]["RoundedCylinder"],
  Method -> {"SplinePoints" -> 20}]
Out[55]=

The textual label may contain rasterization artifacts:

In[56]:=
{
 Graphics3D[{ResourceFunction["CylindricalFlange3D"]["Cylinder", "Label" -> "SomeText", RasterSize -> 200, PlotPoints -> 2]}, PlotLabel -> "RasterSize\[Rule]200, PlotPoints\[Rule]2", ImageSize -> Medium, ViewPoint -> Right],
 Graphics3D[{ResourceFunction["CylindricalFlange3D"]["Cylinder", "Label" -> "SomeText", RasterSize -> 20, PlotPoints -> 40]}, PlotLabel -> "RasterSize\[Rule]20, PlotPoints\[Rule]40", ImageSize -> Medium, ViewPoint -> Right]
 }
Out[56]=

Increase the rasterization rate of the label and/or the number of points of the underlying mesh:

In[57]:=
{
 Graphics3D[{ResourceFunction["CylindricalFlange3D"]["Cylinder", "Label" -> {"SomeText", 1, 0}, RasterSize -> 200, PlotPoints -> 20]}, PlotLabel -> "RasterSize\[Rule]200, PlotPoints\[Rule]20", ImageSize -> Medium, ViewPoint -> Right],
 Graphics3D[{ResourceFunction["CylindricalFlange3D"]["Cylinder", "Label" -> {"SomeText", 1, 0}, RasterSize -> 200, PlotPoints -> 40]}, PlotLabel -> "RasterSize\[Rule]200, PlotPoints\[Rule]40", ImageSize -> Medium, ViewPoint -> Right]
 }
In[58]:=

Custom shapes have to be supplied as pure functions, i.e. with head Function:

In[59]:=
ClearAll[thorn]
thorn[len_, rad_] := Cone[{{0, 0, 0}, {0, 0, len}}, rad] &
Graphics3D[{ResourceFunction["CylindricalFlange3D"]["Annulus3D", 0, 0, {15, {thorn, 0.8, 0.1}}]}]
Out[60]=
In[61]:=
ClearAll[thorn]
thorn = Cone[{{0, 0, 0}, {0, 0, #1}}, #2] &;
Graphics3D[{ResourceFunction["CylindricalFlange3D"]["Annulus3D", 0, 0, {15, {thorn, 0.8, 0.1}}]}]
Out[62]=

Neat Examples (2) 

3D art:

In[63]:=
Graphics3D[{ResourceFunction["CylindricalFlange3D"]["Empty", 0, 0, {15, {Cylinder[{{0, 0, 0}, {0, 0, #1}}] &, 0.5, 0.4, 0.15}}]}]
Out[63]=
In[64]:=
gr = ResourceFunction["CylindricalFlange3D"]["Empty", 0, 0, {11, {"Annulus3D", 0.5, 0.4, 0.15}}];
gr2 = {gr, Translate[Rotate[gr, 0.3, {0, 0, 1}], {0, 0, 0.7}]};
Graphics3D[{Translate[gr2, {0, 0, 0}], Translate[gr2, {0, 0, 1.4}], Translate[gr2, {0, 0, 2 1.4}]}]
In[65]:=

Simple clockwork:

In[66]:=
p1 = ResourceFunction[
   "CylindricalFlange3D"][{"Annulus3D", 0.5, 2}, {8, {"Cylinder", 5, 0.3}}];
p2 = ResourceFunction[
   "CylindricalFlange3D"][{"Cylinder", 0.5, 2}, {1, {"Cylinder", 1.2, 0.2}}];
p3 = ResourceFunction[
   "CylindricalFlange3D"][{"Cylinder", 1.2}, {1, {"Cylinder", 3.4, 0.2}}, {1, {"Cylinder", 3, 0.2}}, {8, {"Cuboid", 0.5, 0.15, 0.4}}];
Manipulate[
 Graphics3D[{Orange, Rotate[p1, \[Alpha], {0, 0, 1}], Translate[Rotate[p2, \[Alpha], {0, 0, 1}], {0, 0, 5.1}], Translate[
    Rotate[p3, -\[Alpha] - 0.4, {0, 0, 1}], {0, -3, 2.5}]}], {\[Alpha], 0, 2 \[Pi]}]
Out[67]=

Publisher

Jaroslav Kysela

Requirements

Wolfram Language 13.0 (December 2021) or above

Version History

  • 1.0.0 – 28 June 2023

Related Resources

Author Notes

A possible improvement could be the introduction of the fourth coordinate to the specification of protrusions, which "coordinate" would specify the relative rotation of the given protrusion w.r.t. to its angular position.

License Information