Drawing a cuboid with rounded corners

ClearAll[roundedCuboidF]
roundedCuboidF[hprof_: 10, vprof_: 10, taper_: 1][box_] := 
    ChartElementDataFunction["DoubleProfileCube", "HorizontalProfile" -> hprof, 
                            "VerticalProfile" -> vprof, "TaperRatio" -> taper][box]

Graphics3D[roundedCuboidF[][{{0, 1}, {0, 1}, {0, 1}}], Boxed -> False]

enter image description here

or

ContourPlot3D[Norm[{x, y, z}, 4], {x, -1, 1}, {y, -1, 1}, {z, -1, 1},
  Mesh -> None, Boxed -> False, Axes -> False, Lighting -> "Neutral",
  ContourStyle ->  Directive[Orange, Opacity[0.8], Specularity[White, 30]]]

enter image description here


Two more options. These allow direct control of the box dimensions and the rounding radius.


Using ContourPlot3D:

signedDistance[p_, p0_, p1_] := Piecewise[
  {{-Min[p - p0, p1 - p], And @@ Thread[p0 <= p <= p1]}, 
   {EuclideanDistance[p, MapThread[Min[Max[#1, #2], #3] &, {p, p0, p1}]], True}}]
roundedCuboidPlot[p0 : {x0_, y0_, z0_}, p1 : {x1_, y1_, z1_}, r_, opts : OptionsPattern[]] := 
 ContourPlot3D[signedDistance[{x, y, z}, p0 + r, p1 - r] == r, 
  {x, x0 - r, x1 + r}, {y, y0 - r, y1 + r}, {z, z0 - r, z1 + r}, opts]

roundedCuboidPlot[{0, 0, 0}, {1, 2, 3}, 1/4, BoxRatios -> Automatic, Mesh -> None]

enter image description here


Using Graphics3D primitives:

roundedCuboid[p0 : {x0_, y0_, z0_}, p1 : {x1_, y1_, z1_}, r_] := 
 {EdgeForm[None], 
  Cuboid[p0 + {0, r, r}, p1 - {0, r, r}], 
  Cuboid[p0 + {r, 0, r}, p1 - {r, 0, r}], 
  Cuboid[p0 + {r, r, 0}, p1 - {r, r, 0}], 
  Table[Cylinder[{{x0 + r, y, z}, {x1 - r, y, z}}, r], 
   {y, {y0 + r, y1 - r}}, {z, {z0 + r, z1 - r}}], 
  Table[Cylinder[{{x, y0 + r, z}, {x, y1 - r, z}}, r], 
   {x, {x0 + r, x1 - r}}, {z, {z0 + r, z1 - r}}], 
  Table[Cylinder[{{x, y, z0 + r}, {x, y, z1 - r}}, r], 
   {x, {x0 + r, x1 - r}}, {y, {y0 + r, y1 - r}}], 
  Table[Sphere[{x, y, z}, r], 
   {x, {x0 + r, x1 - r}}, {y, {y0 + r, y1 - r}}, {z, {z0 + r, z1 - r}}]}

Graphics3D[{roundedCuboid[{0, 0, 0}, {1, 2, 3}, 1/4]}]

enter image description here


Rahul's otherwise fine approach has a drawback that can be seen if you include an Opacity[] directive:

Graphics3D[{Opacity[2/3, Pink], roundedCuboid[{0, 0, 0}, {1, 2, 3}, 1/4]},
           Boxed -> False]

look at 'em ribs!

The "ribs" may or may not be desirable in an application, so I sought an alternative that does not use too many Polygon[]s (as with the solutions based on plotting) and yet looks fine when made translucent.

The following routine is not quite Mr. Wizard's wish in the comments, but it is certainly built from BSplineSurface[] + Polygon[] components:

roundedCuboid[p1_?VectorQ, p2_?VectorQ, r_?NumericQ] := 
       Module[{csk, csw, cv, ei, fi, ocp, osk, owt},
              cv = Tuples[Transpose[{p1 + r, p2 - r}]];
              ocp = {{{1, 0, 0}, {1, 1, 0}, {0, 1, 0}},
                     {{1, 0, 1}, {1, 1, 1}, {0, 1, 1}},
                     {{0, 0, 1}, {0, 0, 1}, {0, 0, 1}}};
              osk = {{0, 0, 0, 1, 1, 1}, {0, 0, 0, 1, 1, 1}};
              owt = {{1, 1/Sqrt[2], 1}, {1/Sqrt[2], 1/2, 1/Sqrt[2]},
                     {1, 1/Sqrt[2], 1}};
              ei = {{{4, 8}, {2, 6}, {1, 5}, {3, 7}},
                    {{6, 8}, {2, 4}, {1, 3}, {5, 7}},
                    {{7, 8}, {3, 4}, {1, 2}, {5, 6}}};
              csk = {{0, 0, 1, 1}, {0, 0, 0, 1, 1, 1}};
              csw = {{1, 1/Sqrt[2], 1}, {1, 1/Sqrt[2], 1}};
              fi = {{8, 6, 5, 7}, {8, 7, 3, 4}, {8, 4, 2, 6},
                    {4, 3, 1, 2}, {2, 1, 5, 6}, {1, 3, 7, 5}};
              Flatten[{EdgeForm[], BSplineSurface3DBoxOptions ->
                                   {Method -> {"SplinePoints" -> 35}}, 
                       MapIndexed[BSplineSurface[Map[
                       AffineTransform[{RotationMatrix[π Mod[#2[[1]] - 1, 4]/2,
                                        {0, 0, 1}], #1}], 
                       ocp.DiagonalMatrix[r {1, 1, If[Mod[#2[[1]] - 1, 8] < 4,
                                                      1, -1]}], {2}], 
                       SplineDegree -> 2, SplineKnots -> osk, SplineWeights -> owt] &,
                       cv[[{8, 4, 2, 6, 7, 3, 1, 5}]]], 
                       MapIndexed[Function[{idx, pos}, 
                          BSplineSurface[Outer[Plus, cv[[idx]], 
                          Composition[Insert[#, 0, pos[[1]]] &, 
                                      RotationTransform[π (pos[[2]] - 1)/2]] /@
                          (r {{1, 0}, {1, 1}, {0, 1}}), 1], SplineDegree -> {1, 2},
                          SplineKnots -> csk, SplineWeights -> csw]], ei, {2}], 
                       Polygon[MapThread[Map[TranslationTransform[r #2], cv[[#1]]] &,
                               {fi, Join[#, -#] &[IdentityMatrix[3]]}]]}]]

Using this version instead in the first snippet yields the following picture:

no ribs, ma!

Some more examples:

Graphics3D[{Yellow, roundedCuboid[{0, 0, 0}, {1, 3, 1}, 1/10],
            Blue, roundedCuboid[{2, 1, 1}, {4, 2, 3}, 1/4]}, Boxed -> False]

still life of two rounded cuboids

Graphics3D[{{EdgeForm[Gray], Opacity[1/2, Green], Cuboid[{2, 1, 1}, {4, 2, 3}]},
            {Pink, roundedCuboid[{2, 1, 1}, {4, 2, 3}, 1/5]}},
           Boxed -> False, Lighting -> "Neutral"]

peek-a-boo

Tags:

Graphics3D