(* barycenter of a polygon *)
averagepoints[points_?MatrixQ] :=
Mean[If[TrueQ[First[points] == Last[points]], Most, Identity][points]]
(* Newell's algorithm for face normals *)
newellNormals[pts_?MatrixQ] := Module[{tp = Transpose[pts]}, Normalize[MapThread[Dot,
{RotateLeft[ListConvolve[{{-1, 1}}, tp, {-1, -1}]],
RotateRight[ListConvolve[{{1, 1}}, tp, {-1, -1}]]}]]]
thickaux[points_thickenaux[points_, outer_, thick_] := Module[{center = averagepoints[points],
nrm = newellNormals[points], outerpoints, radialpoints},
outerpoints = Map[(center - thick nrm + outer (# - center)) &, points];
radialpoints = MapThread[Join[Reverse[#1], #2] &,
Map[Partition[#, 2, 1, {1, 1}] &, {points, outerpoints}]];
Flatten[{Polygon[points], Polygon /@ radialpoints, Polygon[Reverse[outerpoints]]}]]
ThickenPolygons[shape_, outer_: 0.8, thick_: 0.04] :=
shape /. Polygon[p_?MatrixQ] :> thickaux[pthickenaux[p, outer, thick]
I set the defaults in ThickenPolygons[]
to work for tetrahedra; for other polyhedra or for parametrically-defined surfaces, you might need to play around with the values for the parameters outer
and thick
.
Here's an alternate version, which might be more suitable for polyhedra than the previous version:
thickenaux[points_, outer_, fac_] :=
Module[{center = averagepoints[points], nrm = newellNormals[points], n, outerpoints,
radialpoints, thick},
n = Length[points] - Boole[TrueQ[First[points] == Last[points]]];
thick = fac (1 - outer) Mean[Norm[# - center] & /@ points];
outerpoints = Map[(center - thick nrm + outer (# - center)) &, points];
radialpoints = MapThread[Join[Reverse[#1], #2] &,
Map[Partition[#, 2, 1, {1, 1}] &, {points, outerpoints}]];
Flatten[{Polygon[points], Polygon /@ radialpoints, Polygon[Reverse[outerpoints]]}]]
ThickenPolygons[shape_, outer_: 0.8, fac_: Sqrt[2]/4] :=
shape /. Polygon[p_?MatrixQ] :> thickenaux[p, outer, fac]
If you run this version of ThickenPolygons[]
on spikeyCut
and carefully inspect the coordinates, you'll see that the points in the inner walls match up perfectly; there are neither gaps nor unintentional polygon intersections.
The proper value of fac
to use will depend on the polyhedron being considered. For the Platonic solids ($\phi$ denotes the golden ratio),
\begin{array}{c|c}
\text{polyhedron}&\class{code}{\text{fac}}\\\hline
\text{cube}&\frac1{\sqrt 2}\\
\text{dodecahedron}&\frac{1+\phi}{2}\\
\text{icosahedron}&\frac{1+\phi}{2}\\
\text{octahedron}&\frac1{\sqrt 2}\\
\text{tetrahedron}&\frac1{2\sqrt 2}
\end{array}
The default value used for fac
works for the tetrahedron and the spikey.