Function Repository Resource:

RoundedFrame3D

Source Notebook

Represent a 3D frame with rounded edges

Contributed by: Jaroslav Kysela

ResourceFunction["RoundedFrame3D"][{dx,dy,dz}]

represents a 3D frame with dimensions {dx,dy,dz}.

ResourceFunction["RoundedFrame3D"][{dx,dy,dz},{tx,ty}]

sets the thickness of the frame in x- and y-directions.

Details and Options

ResourceFunction["RoundedFrame3D"] can be used in Graphics3D.
Graphics rendering is affected by directives such as FaceForm, Opacity, and color.
ResourceFunction["RoundedFrame3D"][] is equivalent to ResourceFunction["RoundedFrame3D"][{1,1,1}, {0.4,0.4}].
ResourceFunction["RoundedFrame3D"][d] is equivalent to ResourceFunction["RoundedFrame3D"][{d,d,d}].
ResourceFunction["RoundedFrame3D"][{dx,dy}] is equivalent to ResourceFunction["RoundedFrame3D"][{dx,dy,0.2}].
ResourceFunction["RoundedFrame3D"][{dx,dy,dz}] is equivalent to ResourceFunction["RoundedFrame3D"][{dx,dy,dz}, 0.4].
ResourceFunction["RoundedFrame3D"][{dx,dy,dz}, t] is equivalent to ResourceFunction["RoundedFrame3D"][{dx,dy,dz}, {t,t}].
The following options can be given:
RoundingRadiusAutomaticradii of rounded edges
"JoinForm""Bevel"form of frame joins
Method"GraphicsComplex"internal representation setting
The RoundingRadius setting can have the following forms:
rrsame radius for interior and exterior edges
{inrr, outrr}separate radii for the interior and exterior edges
The rr setting can have the following forms:
Nonerounding radii set to zero
Automaticrounding radii set to 0.1
rall radii set to r
{rx,ry}equivalent to {rx,ry,0.1}
{rx,ry,rz}set rounding radius for each direction separately
The "JoinForm" setting can have the following forms:
jfsame form for interior and exterior edges
{injf, outjf}separate specification for the interior and exterior edges
The jf setting can have the following values:
"Bevel"blunt joins and corners
"Miter"sharp joins and corners
"Round"round joins
Automaticequivalent to "Bevel"
The Method setting can have the following forms:
"GraphicsComplex"represent the frame as a GraphicsComplex
"List"represent the frame as a List of individual graphics primitives

Examples

Basic Examples (7) 

A unit rounded frame:

In[1]:=
Graphics3D[ResourceFunction["RoundedFrame3D"][]]
Out[1]=

Rounded frames with different sizes:

In[2]:=
Graphics3D[{EdgeForm[], Yellow, ResourceFunction["RoundedFrame3D"][{2, 1, 0.5}, 0.2, RoundingRadius -> 0.1], Blue, Translate[
   ResourceFunction["RoundedFrame3D"][{0.5, 1, 1}, 0.2, RoundingRadius -> 0.1], {1, 1, 0}]}]
Out[2]=

Differently styled rounded frames:

In[3]:=
{Graphics3D[{Pink, ResourceFunction["RoundedFrame3D"][]}], Graphics3D[{Opacity[0.5], ResourceFunction["RoundedFrame3D"][]}], Graphics3D[{Specularity[5], ResourceFunction["RoundedFrame3D"][]}]}
Out[3]=

Rotated rounded frame:

In[4]:=
Graphics3D[
 Rotate[ResourceFunction["RoundedFrame3D"][], \[Pi]/3, {1, -1, 1}]]
Out[4]=

Simple frame:

In[5]:=
Graphics3D[
 ResourceFunction["RoundedFrame3D"][{2, 1, 0.2}, 0.2, RoundingRadius -> 0]]
Out[5]=

Rounded edges:

In[6]:=
Graphics3D[{EdgeForm[], ResourceFunction["RoundedFrame3D"][{1.2, 1, 0.5}, 0.3, RoundingRadius -> 0.1]}, ViewPoint -> Above, ViewVertical -> {0, 1, 0}]
Out[6]=

Different join forms:

In[7]:=
Graphics3D[{Red, ResourceFunction["RoundedFrame3D"][{2.5, 3, 1}, {0.7, 0.9}, RoundingRadius -> {0.3, 0.2}, "JoinForm" -> #]}, Boxed -> False] & /@ {"Bevel", "Miter", "Round"}
Out[7]=

Scope (12) 

Dimensions (3) 

A rounded frame parallel to each axis:

In[8]:=
Graphics3D[ResourceFunction["RoundedFrame3D"][{3, 2, 1}]]
Out[8]=

Modify thickness in the x-direction:

In[9]:=
Table[Graphics3D[{ResourceFunction[
    "RoundedFrame3D"][{1.8, 2.5, 0.4}, {tx, 0.5}]}, ViewPoint -> Above, PlotLabel -> "x-axis thickness = " <> ToString[tx]], {tx, 0.2, 1, 0.3}]
Out[9]=

Modify thickness in the y-direction:

In[10]:=
Table[Graphics3D[{ResourceFunction[
    "RoundedFrame3D"][{1.8, 2.5, 0.4}, {0.5, ty}]}, ViewPoint -> Above, PlotLabel -> "y-axis thickness = " <> ToString[ty]], {ty, 0.2, 1.5, 0.5}]
Out[10]=

Position and rotation (3) 

The frame is centered at the origin:

In[11]:=
Graphics3D[ResourceFunction["RoundedFrame3D"][{3, 2, 1}], Axes -> True]
Out[11]=

Use Translate to move the frame away from the center:

In[12]:=
Graphics3D[{Red, ResourceFunction["RoundedFrame3D"][{1, 1, 0.3}], Blue, Translate[
   ResourceFunction["RoundedFrame3D"][{1, 1, 0.3}], {3, 0, 0}]}, Axes -> True, PlotRange -> {{-1, 4}, {-1, 1}, {-1, 1}}]
Out[12]=

Use Rotate to rotate the frame:

In[13]:=
Graphics3D[
 Rotate[ResourceFunction[
   "RoundedFrame3D"][{1, 2, 0.3}], -30 Degree, {0, 1, 1}], Axes -> True]
Out[13]=

Styling (6) 

Color directives specify the face colors of frames:

In[14]:=
Table[Graphics3D[{c, ResourceFunction["RoundedFrame3D"][]}], {c, {Red,
    Green, Blue, Yellow}}]
Out[14]=

Different properties can be specified for the front and back of faces using FaceForm:

In[15]:=
Graphics3D[{FaceForm[Yellow, Blue], ResourceFunction["RoundedFrame3D"][{2, 2, 0.6}]}, PlotRange -> 1.1 {{-1, 1}, {0, 1}, {-1, 1}}]
Out[15]=

Opacity specifies the face opacity:

In[16]:=
Table[Graphics3D[{Opacity[o], ResourceFunction["RoundedFrame3D"][]}, Boxed -> False], {o, {0.1, 0.5, 0.9}}]
Out[16]=

Suppress display of the bounding box:

In[17]:=
Graphics3D[{Opacity[0.6], EdgeForm[], ResourceFunction["RoundedFrame3D"][{1, 1.1, 1}, {0.3, 0.3}]}, Boxed -> False]
Out[17]=

Decompose the frame into its components and apply different styling to each:

In[18]:=
frame = ResourceFunction["RoundedFrame3D"][{2.4, 2, 1}, 0.5, RoundingRadius -> 0.2, Method -> "List"];
Graphics3D[{{Opacity[0.6, Blue], frame[[1]]}, {Red, frame[[2]]}, Green, frame[[3]], Yellow, frame[[4]], Orange, frame[[5]], White, frame[[7]]}, Boxed -> False]
Out[19]=

Apply textures:

In[20]:=
frame = ResourceFunction["RoundedFrame3D"][{2, 2.2, 2}, {0.3, 0.18}, "JoinForm" -> "Miter", RoundingRadius -> 0];
img = ImageRotate[
   ExampleData[{"ColorTexture", "WavesPattern"}], \[Pi]/2];
Graphics3D[{Gray, Texture[img], frame /. Polygon[x__] :> Polygon[x, VertexTextureCoordinates -> {{0, 0}, {1, 0}, {1, 1}, {0, 1}}]}, Lighting -> "Neutral", Boxed -> False]
Out[21]=

Options (10) 

RoundingRadius (5) 

Different rounding radii:

In[22]:=
Table[Graphics3D[{EdgeForm[], ResourceFunction["RoundedFrame3D"][{1, 1, 1}, 0.4, RoundingRadius -> rr, "JoinForm" -> "Bevel"]}, PlotLabel -> "RoundingRadius \[Rule] " <> ToString[rr], ImageSize -> Small, Boxed -> False], {rr, 0, 0.2, 0.1}]
Out[22]=

Different radius for the interior and exterior edges:

In[23]:=
Graphics3D[{EdgeForm[], ResourceFunction["RoundedFrame3D"][{2, 1.5, 0.4}, 0.5, RoundingRadius -> {0.2, 0.05}]}, Boxed -> False]
Out[23]=

Only interior edges rounded:

In[24]:=
Graphics3D[{EdgeForm[], ResourceFunction["RoundedFrame3D"][{1, 1, 0.4}, 0.2, RoundingRadius -> {0.2, 0}]}, Boxed -> False]
Out[24]=

Rounded radius only in some directions:

In[25]:=
Graphics3D[{EdgeForm[], ResourceFunction["RoundedFrame3D"][{3, 2, 0.5}, 0.4, RoundingRadius -> {{0.2, 0.2, 0}, 0}, "JoinForm" -> "Round"]}]
Out[25]=

Specify radii for each direction for both interior and exterior edges separately:

In[26]:=
Graphics3D[{EdgeForm[Black], ResourceFunction["RoundedFrame3D"][{3, 2, 1}, 0.4, RoundingRadius -> {{0.1, 0.2, 0.3}, {0.3, 0.2, 0.1}}, "JoinForm" -> "Round"]}]
Out[26]=

JoinForm (2) 

Choose the join form of the corners:

In[27]:=
Table[Graphics3D[{EdgeForm[], ResourceFunction["RoundedFrame3D"][{2.4, 2, 0.6}, 0.5, RoundingRadius -> 0.2, "JoinForm" -> cf]}, PlotLabel -> cf, ViewPoint -> Above], {cf, {"Bevel", "Miter", "Round"}}]
Out[27]=

Interior and exterior edges can have different join form:

In[28]:=
Graphics3D[{EdgeForm[], ResourceFunction["RoundedFrame3D"][{2.4, 2, 0.6}, 0.5, RoundingRadius -> 0.15, "JoinForm" -> {"Miter", "Round"}]}, ViewPoint -> Above]
Out[28]=

The internal structure of the frame differs for each "JoinForm":

In[29]:=
types = {"Bevel", "Miter", "Round"};
TableForm[
 Table[Graphics3D[{EdgeForm[Thick], ResourceFunction["RoundedFrame3D"][2 {1, 1, 1}, 0.7, RoundingRadius -> 0.2, "JoinForm" -> {ic, oc}] /. EdgeForm[] -> EdgeForm[Thick]}, ViewPoint -> Above, ImageSize -> Tiny, Boxed -> False], {ic, types}, {oc, types}], TableHeadings -> {"inner: " <> # & /@ types, "outer: " <> # & /@ types}, TableAlignments -> Center]
Out[30]=

Method (3) 

By default, the frame is returned as a single instance of GraphicsComplex:

In[31]:=
frame = ResourceFunction["RoundedFrame3D"][{2.4, 2, 1}, 0.5, RoundingRadius -> 0.2];
Short[frame]
Out[32]=
In[33]:=
Graphics3D[frame]
Out[33]=

To get access to individual parts of the frame, set Method to "List":

In[34]:=
frame = ResourceFunction["RoundedFrame3D"][{2.4, 2, 1}, 0.5, RoundingRadius -> 0.2, Method -> "List"];
Short[frame]
Out[35]=

This way, each part can be styled differently:

In[36]:=
Graphics3D[{Blue, frame[[1]], Red, frame[[2]], Green, frame[[3]], Yellow, frame[[4]], Orange, frame[[5]], White, frame[[7]]}, Boxed -> False]
Out[36]=

Applications (5) 

Cube with a hole:

In[37]:=
Graphics3D[{ResourceFunction["RoundedFrame3D"][
   4 {1, 1, 1}, {1.4, 1.4}, RoundingRadius -> 0]}, Boxed -> False]
Out[37]=

Door frame:

In[38]:=
Graphics3D[{EdgeForm[], Brown, ResourceFunction["RoundedFrame3D"][{2, 3.2, 0.6}, 0.2, RoundingRadius -> {0.1, 0}, "JoinForm" -> "Miter"]}, ViewPoint -> Above]
Out[38]=

Window bars:

In[39]:=
Graphics3D[{EdgeForm[], Orange, ResourceFunction["RoundedFrame3D"][{17, 17, 2}, 3, RoundingRadius -> 0], Lighter@Gray, ResourceFunction["RoundedFrame3D"][{11, 11, 2}, 0.5, RoundingRadius -> 0], Gray, Translate[
   ResourceFunction["RoundedFrame3D"][{10, 2, 1}, 0.5, RoundingRadius -> {{0, 0.5, 0.5}, 0}], Table[{0, 2 i, 0}, {i, -2, 2}]]}, Lighting -> "Neutral", ViewVertical -> {-1, 0, 0}]
Out[39]=

Simple model of a house with a chimney:

In[40]:=
(* Evaluate this cell to get the example input *) CloudGet["https://www.wolframcloud.com/obj/fd471198-7275-4057-a6e2-657e0726bab4"]
Out[41]=

Picture frame:

In[42]:=
(* Evaluate this cell to get the example input *) CloudGet["https://www.wolframcloud.com/obj/d7f1858b-5940-46cb-a774-73457afa9b78"]
Out[42]=

Properties and Relations (5) 

RoundedFrame3D can mimic Torus:

In[43]:=
With[{r = 0.2}, {
  Graphics3D[{EdgeForm[], ResourceFunction["RoundedFrame3D"][{6 r, 6 r, 2 r}, 2 r, RoundingRadius -> r, "JoinForm" -> "Round"]}, ViewPoint -> Above],
  Graphics3D[{EdgeForm[], Torus[{0, 0, 0}, {r, 3 r}]}, ViewPoint -> Above]
  }]
Out[43]=

RoundedFrame3D can mimic RoundedAnnulus3D:

In[44]:=
With[{r = 0.1, t = 0.5, hz = 0.5}, {
  Graphics3D[{EdgeForm[], ResourceFunction["RoundedFrame3D"][2 {r + t, r + t, hz}, {t, t}, RoundingRadius -> r, "JoinForm" -> "Round"]}, Boxed -> False], Graphics3D[{EdgeForm[], ResourceFunction[
      "RoundedAnnulus3D"][{{0, 0, -hz}, {0, 0, hz}}, {r, r + t}, RoundingRadius -> r]}, Boxed -> False]
  }]
Out[44]=

RoundedFrame3D can mimic RoundedCylinder:

In[45]:=
With[{r = 0.2, t = 0.5, hz = 0.5}, {
  Graphics3D[{EdgeForm[], ResourceFunction["RoundedFrame3D"][2 {t, t, hz}, {t, t}, RoundingRadius -> {0, r}, "JoinForm" -> "Round"]}, Boxed -> False], Graphics3D[{EdgeForm[], ResourceFunction["RoundedCylinder"][{{0, 0, -hz}, {0, 0, hz}}, t, RoundingRadius -> r]}, Boxed -> False]
  }]
Out[45]=

RoundedFrame3D can mimic RoundedCuboid:

In[46]:=
With[{hx = 1, hy = 1.2, hz = 1.5, tx = 1, ty = 1.1, rzo = 0.1}, {
  Graphics3D[{EdgeForm[], ResourceFunction["RoundedFrame3D"][2 {hx, hy, hz}, {tx, ty}, RoundingRadius -> {0, rzo}, "JoinForm" -> "Bevel"]}, Boxed -> False], Graphics3D[{EdgeForm[], ResourceFunction["RoundedCuboid"][-{hx, hy, hz}, {hx, hy, hz}, RoundingRadius -> rzo]}, Boxed -> False]
  }]
Out[46]=

RoundedFrame3D can mimic Tube with a rectangular layout:

In[47]:=
With[{r = 0.15, d = 0.4},
 pts = {{-1, 1, 0}, {-1 + d, 1, 0}, {1 - d, 1, 0}, {1, 1, 0}, {1, 1 - d, 0}, {1, -1 + d, 0}, {1, -1, 0}, {1 - d, -1, 0}, {-1 + d, -1, 0}, {-1, -1, 0}, {-1, -1 + d, 0}, {-1, 1 - d, 0}};
 {
  Graphics3D[{EdgeForm[], ResourceFunction["RoundedFrame3D"][2 {1 + r, 1 + r, r}, 2 r, RoundingRadius -> r, "JoinForm" -> "Round"]}, ViewPoint -> Above], Graphics3D[{Tube[
     BSplineCurve[pts, SplineClosed -> True, SplineWeights -> {1/Sqrt[2], 1, 1, 1/Sqrt[2], 1, 1, 1/Sqrt[2], 1, 1, 1/Sqrt[2], 1, 1}, SplineDegree -> 2], r]}, ViewPoint -> Above]
  }
 ]
Out[47]=

Possible Issues (3) 

When dimensions, thicknesses and rounding radii are inconsistent, no frame is produced and an error message is printed:

In[48]:=
Graphics3D[ResourceFunction["RoundedFrame3D"][{4, 2, 0.2}, {3, 0.3}]]
Out[48]=

Here, the x-axis thickness exceeds half the x-axis dimension. Adjust the thickness:

In[49]:=
Graphics3D[ResourceFunction["RoundedFrame3D"][{4, 2, 0.2}, {1, 0.3}]]
Out[49]=

The chosen "JoinForm" value affects the constraints:

In[50]:=
Table[Graphics3D[
  ResourceFunction["RoundedFrame3D"][{2, 2, 0.2}, {1, 0.5}, RoundingRadius -> 0.1, "JoinForm" -> cf], PlotLabel -> cf], {cf, {"Bevel", "Miter", "Round"}}]
Out[50]=

The flat-face surface has a different structure when "JoinForm" is set to {"Round","Round"}:

In[51]:=
Graphics3D[{EdgeForm[Thick], ResourceFunction["RoundedFrame3D"][2 {1, 1, 1}, 0.5, RoundingRadius -> 0.01, "JoinForm" -> {#, "Round"}] /. EdgeForm[] -> EdgeForm[Thick]}, ViewPoint -> Above, PlotLabel -> {#, "Round"}, Boxed -> False] & /@ {"Bevel", "Miter", "Round"}
Out[51]=

This allows for narrow corners:

In[52]:=
Graphics3D[{EdgeForm[Thick], ResourceFunction["RoundedFrame3D"][2 {1, 1, 1}, 0.5, RoundingRadius -> 0.22, "JoinForm" -> {#, "Round"}] /. EdgeForm[] -> EdgeForm[Thick]}, ViewPoint -> Above, PlotLabel -> {#, "Round"}] & /@ {"Bevel", "Miter", "Round"}
Out[52]=

Neat Examples (2) 

Interlaced frames:

In[53]:=
frame = ResourceFunction[
   "RoundedFrame3D"][{0.5, 0.6, 0.2}, {0.1, 0.1}, RoundingRadius -> 0.02];
Graphics3D[{Lighter[Blue, 0.8], Table[Rotate[Translate[frame, {0, 0, 1.2}], ang, {1, 0, 0}], {ang, 0, 2 \[Pi], \[Pi]/5}], Table[Rotate[
    Translate[Rotate[frame, \[Pi]/2, {0, 1, 0}], {0, 0, 1.2}], ang + \[Pi]/10, {1, 0, 0}], {ang, 0, 2 \[Pi], \[Pi]/5}]}, Boxed -> False, ViewPoint -> Right]
Out[54]=

Interactively adjust the rounding radii:

In[55]:=
Manipulate[
  Graphics3D[{EdgeForm[Thick], ResourceFunction["RoundedFrame3D"][4 {1, 1, 1}, {1.4, 1.4}, RoundingRadius -> {{rxi, ryi, rzi}, {rxo, ryo, rzo}}]}, Boxed -> False, ImageSize -> Small], {{rxi, 0.2}, 0, 0.7}, {{ryi, 0.3}, 0, 0.7}, {{rzi, 0.5}, 0, 0.7}, Delimiter, {{rxo, 0}, 0, 0.7}, {{ryo, 0.1}, 0, 0.7}, {{rzo, 0.2}, 0, 0.7}]
Out[55]=

Publisher

Jaroslav Kysela

Version History

  • 1.0.0 – 22 March 2023

Related Resources

Author Notes

Scaled coordinates are not supported.

License Information