Dual Polyhedra in Grasshopper
Exploring dual polyhedra in Grasshopper is an interesting topic. In this post, I try to generate the dual of any polyhedron using Rhino Python and possibly Grasshopper. I developed this code for Rhino Python earlier here, and now I have converted it into a Grasshopper-Python component for better usability. I start the process by breaking the polyhedron into individual faces and gathering the corner points of each face. These points become the vertices of the dual polyhedron. Then, for each vertex, I connect the centroids of the surrounding faces to create polygons, which form the structure of the dual shape. After forming the polygons, I generate planar surfaces from each one and combine all the resulting faces. Finally, I use Rhino’s Brep tools to join the planar surfaces into a single solid, creating a clean and fully manipulable dual polyhedron.

I developed this definition in the Grasshopper version of Rhino 8 (Python 3). This means it will NOT work in Rhino 7. Just place a Python 3 component in Grasshopper, and change the x and y inputs by erasing one of them. B will be the only input of this component. Then, right-click on the B input and choose “List Access” and “Brep” as the type, and turn on “Required”. It accepts lists, so you can process many solids at once. dualBrep is the only output that returns the calculated dual polyhedron (a closed polysurface). Feed the component with a Box (fingers crossed); it should give you an Octahedron. The output is ready for 3d printing. Below is the Python code you need to paste inside a Python 3 component in Grasshopper:
import rhinoscriptsyntax as rs
import Rhino.Geometry as rg
dualPoints=[]
dualFaces=[]
dualBrep=None
if B:
result=rs.ExplodePolysurfaces(B)
points=[]
for surf in result:
for poly in rs.DuplicateSurfaceBorder(surf):
pts=rs.PolylineVertices(poly)
if pts: points.extend(pts)
dualPoints=rs.CullDuplicatePoints(points)
for pp in dualPoints:
newface=[rs.SurfaceAreaCentroid(surf)[0] for surf in result if rs.IsPointOnSurface(surf,pp)]
newface=rs.SortPointList(newface)
if len(newface)>=3:
if newface[0]!=newface[-1]: newface.append(newface[0])
pline=rg.Polyline(newface)
if not pline.IsClosed: pline.Add(pline[0])
crv=pline.ToNurbsCurve()
breps=rg.Brep.CreatePlanarBreps([crv])
if breps: dualFaces.extend(breps)
if dualFaces:
joined=rg.Brep.JoinBreps(dualFaces,1e-6)
if joined: dualBrep=joined[0]

