Self-folding origami, an innovation where flat sheets fold themselves into three-dimensional structures, is highly applicable in aerospace, medicine, and construction. However, creation of these structures relies on knowledge of a crease pattern that folds into the target structure. This project develops a computational method for unfolding and generating crease patterns of orthogonal polyhedra: solids with faces that only meet at right angles. The 3D structure is modified to produce a voxelized surface mesh, where we extract its faces, encode their adjacency and fold angle values, to a graph. We then repeatedly extract spanning trees, selecting based on crease pattern perimeter and non-overlapping nets to produce the most “efficient” crease patterns across a range of orthogonal polyhedra.

Introduction

The traditional art of origami folds a flat sheet of paper into the form of a three dimensional object. In modern applications, self-folding origami automates this step. Source material sheets are embedded with actuators along crease lines that allow for the material to fold itself into the shape. Through careful design, structures can be optimized for different parameters in both folded and unfolded states, and their diverse abilities have led to applications in aerospace, medicine, and construction. These applications share a common inverse problem of deriving a crease pattern that, when folded, produces a desired three-dimensional shape. The crease pattern must satisfy the constraints of lying on a single connected sheet, and the pattern must not overlap itself when unfolded. Reliable algorithms for producing such unfoldings from the 3D shape are crucial for self-folding design.
Whether every convex polyhedron admits a non-overlapping unfolding along its edges is an open question, known as Dürer's problem. For non-convex polyhedra the answer is known to be no: there exist shapes, including orthogonal ones, with no valid edge unfolding at all (O’Rourke, 2008). This project therefore restricts the problem to orthogonal polyhedra represented on a unit grid, where every face is a unit square and every dihedral angle is 90° or 270°. This gridded structure both makes the search for crease patterns tractable and enlarges the set of allowed cuts beyond the original polyhedron’s edges, for circumventing the non-convex unfolding results.

Key Terms

◼
  • Orthogonal Polyhedra:
  • ◼
  • A polyhedron whose faces are all perpendicular to one of three Cartesian coordinate axes. Every dihedral angle is either 90 or 270 degrees
  • ◼
  • Voxelization:
  • ◼
  • The approximation of a solid region by unit cubes drawn on a 3D grid. A cube is included when its center lies inside of the objects region.
  • ◼
  • Crease Pattern:
  • ◼
  • A set of lines on a flat sheet along which it is folded. Color represents the direction of the fold
  • ◼
  • Mountain Fold:
  • ◼
  • A crease folded so that two adjacent faces rotate away from the viewer, forming a ridge. Typically depicted as a red line.
  • ◼
  • Valley Fold:
  • ◼
  • A crease folded so that two adjacent faces rotate towards the viewer, forming a trough. Typically depicted as a blue line.
    ​
  • Objectives

    The objectives of this computational essay were to:
    ◼
  • Implement an algorithmic methodology for unfolding orthogonal polyhedra
  • ◼
  • Explore the algorithm’s production of valid nets and material efficiency
  • ◼
  • Develop a robust platform for flat folding origami design.
  • Voxelizing the input object

    To begin constructing the crease pattern from a 3D object, the boundary mesh versions of a list of example solids are imported from the Wolfram Cloud . The example objects were created in the CAD software Onshape and exported as STL files. In most experiments, Soma cube pieces were used because their regular structure and orthogonal geometry make them well suited to this unfolding method.
    Launch the kernels for enabling parallel computing in the rest of the program.
    In[]:=
    Quiet[LaunchKernels[]];
    Import the 3D STL files as boundary mesh regions from the cloud. Choose the index of the object to unfold.
    In[]:=
    solids=CloudGet["https://www.wolframcloud.com/obj/fc93a93a-8b8a-4a3c-abd1-17b419537259"]​​index=9
    Out[]=
    
    ,
    ,
    ,
    ,
    ,
    ,
    ,
    ,
    ,
    ,
    ,
    ,
    ,
    ,
    ,
    ,
    
    Out[]=
    9
    STL’s are imported dimensionless, so they are scaled by a factor of 1000 for measurement in later functions.
    In[]:=
    solid=Scale[solids[[index]],1000];
    Define a bounding box that contains the maximum dimensions of the 3D object.
    In[]:=
    {{x0,x1},{y0,y1},{z0,z1}}=RegionBounds[solid];
    A variable n is defined as the “resolution” or number of blocks per side length of the object. This parameter is increased for 3D models with finer-grained details. Based on the max side length, h, the length of the unit cube is assigned a value.
    In[]:=
    n=3;​​h=Max[x1-x0,y1-y0,z1-z0]/n;
    Next, generate a list of all points that fit inside of the polygon’s bounding rectangle. Use the pick function to select all of the centers which are inside of the mesh boundary region.
    In[]:=
    centers=Flatten[Table[{x,y,z},{x,x0+h/2,x1,h},{y,y0+h/2,y1,h},{z,z0+h/2,z1,h}],2];​​inside=Pick[centers,RegionMember[solid][centers]];
    Generate cuboids for each defined center point, then unionize the volume to have a single internal volume.
    In[]:=
    voxels=Cuboid[#-h/2,#+h/2]&/@inside;​​voxels=RegionUnion[voxels];
    Visually verify the generated faces.
    In[]:=
    Graphics3D[{Opacity[0.7],voxels}]
    Out[]=
    Create a list of polygon objects that make up the mesh.
    In[]:=
    allFaces=MeshPrimitives[voxels,2];

    Manifold Mesh verification

    To verify that the voxelized surface is suitable for unfolding, we perform two checks. First, we confirm the mesh is manifold, where every edge must be shared by exactly two faces, since an edge bordering three or more faces has no well-defined fold. Second, we confirm the surface has genus 0 using the Euler characteristic V − E + F.
    ​
    Find the edges that have any number of faces greater than 2, as that would mean they’d be impossible to fold. If blank, then mesh is valid.
    In[]:=
    nonManifold=Select[facesPerEdge,Length[#]>2&]
    Out[]=
    
    V-E+F2
    Where
    V
    is the number of vertices of the polygon,
    E
    is the number of edges, and
    F
    is the number of faces.
    Calculate faces, vertices, and edges for the Euler Characteristic Equation.
    In[]:=
    faces=Length[allFaces];​​vertices=MeshCellCount[BoundaryMesh[voxels],0];​​edges=MeshCellCount[voxels,1];​​vertices-edges+faces

    Encoding the Surface as a Face-Adjacency Graph

    To unfold the polyhedron, we must decide what edges of the polygon to cut, and which to keep as creases. Each face in the shape must have at least one connection to another face, without any cycles, since cycles would force the face to connect in two conflicting ways. An acyclic set of edges that are connected to every vertex is a spanning tree. Building a graph whose vertices are the faces of the solid and finding the corresponding spanning trees of that graph shifts the geometric problem to a search over trees.

    Face Centroids and Outward Normals

    Generate the center points of each coordinate for the polygon vertices and their normal vectors. Calculate center points by averaging the squares vertices.
    Determine polarity by selecting a point 1/8th of h away from the center point and determining if it is still contained within the mesh region. If it is, then the normal is inward, otherwise it flips.

    Classifying Creases as Mountain or Valley

    Define the foldAngle function that takes the dot product between the two normal vectors and returns the fold angle.
    Determine the color that an edge should be depending on the value of the fold angle.
    Round function for floating numbers

    Building the Graph

    The obvious way of determining the adjacent faces is comparing every pair of faces and checking if they share an edge, but it costs a polynomial time increase with respect to the number of faces.
    Retrieve the polygon face’s coordinates, while rounding them to precision.
    Group the faces that share an edge by assigning them a key depending on their vertices.
    We make every edge that has a length of 2 an undirected edge, removing duplicates.
    Generate the graph given the edges.

    Unfolding a Spanning Tree

    The UnfoldOnce Method

    Given a spanning tree and a root face, the unfoldOnce function lays the surface flat one face at a time. A depth-first traversal from the root determines the placement order, ensuring each face is consecutive to its parent.. The root establishes the coordinate origin as one of its corners, with the direction of its first edge as the x-axis, and the cross product of the face’s normal and the x-axis as the y-axis. The face’s four vertices are projected into the plane of the root face, yielding their positions in a flat net. The voxel size is used to make each face a unit square.
    Subsequent faces are placed relative to the faces parent. The child and parent meet along a shared edge of the solid, found by the child’s four boundary edges. The connecting edge is the hinge, and since folding doesn’t move a crease, the child will be laid down along the hinge’s position in the plane. The algorithm selects the perpendicular direction pointing away from the parent’s center and the child’s remaining two corners are placed one unit along it. Once added, the child is added to a list of occupied faces. The coordinates are rounded as integers to prevent any floating point comparisons. If any overlap occurs during the assembly, the unfolding is abandoned. Otherwise, the square is recorded and traversal continues.

    Searching the Space of Spanning Trees

    Because a valid net depends entirely upon which spanning tree is chosen, finding one becomes searching through all trees. This randomization is done by assigning random weights to edges of the face-adjacency graph , and taking the minimum spanning tree, producing different trees each iteration. We generate 10,000 of these trees and attempt to unfold each, choosing the ones that flatten without overlap. Evaluation is done in parallel for speed.
    Find the number of possible spanning trees using the Kirchhoff Matrix, then calculate the percentage of the possible search space we explore with 10,000 iterations.
    Utilize the parallel table process to test 10,000 different spanning trees.
    To choose the best tree, the perimeter of the net is used as a metric of the efficiency of the crease pattern. Out of the list of candidates, the net with the minimum perimeter is chosen.

    Visualizing the Crease Pattern

    Once every face is assigned a position based on the optimal graph, the crease pattern is created as a drawing of those faces together, with different folds between them. Each edge of the spanning tree corresponds to a crease shared between a parent face and a child, and the fold angle computed earlier determines the color of the crease that is drawn. Blue depicts a valley fold, red is a mountain fold, and grey dashed is where the two faces are coplanar, but no fold is required.
    Return the two coordinates in a plane of the edge shared by the parent and child, found by intersecting their vertex lists.
    Draw each face as a white square using its assigned position, then overlay the creases on top of each segment with its parent, using fold angle as the determinant of color.
    Determine the data for the crease to function properly. Take the shared segment between the parent and child, then use d as the vector direction of the hinge.

    Refolding the Net

    To assemble the net, each connected edge between faces is assigned data depending on their location and crease direction.
    Returns the fold angle, hinge axis, and a point on the hinge for the crease between a parent and child face, orienting the axis so the child always lies to its left.
    Create the change of hinge data for any path from a vertex of a tree to a child, partition pairs the vertex to vertex connections.
    Use nested transformations to fold the squares beginning with the children node, then moving up to the root.
    This function takes the input parameter t as a measure of the completeness of the fold. For each of the faces, the foldPrim method is called to find the final rotational transform and position in space of the panel. The crease colors are overlaid on top of the crease pattern.
    Generate 40 frames in the ‘folding’ direction and 40 frames in the ‘unfolding’ direction and display them as raster graphics to reduce render lag.

    Evaluating the Crease Patterns

    After the crease pattern is computed, they are evaluated for any overlap, the dimensions of the minimum containing rectangle, and the Hausdorff distance.
    Find if there are any repeated faces that indicate overlap in the crease pattern.
    Determine the minimum dimension unit-square rectangle that can contain the crease pattern.
    Find the Hausdorff distance between the voxelized representation and the STL object.

    Exploring the Spanning tree search space

    We can visualize the exponential rate of increase for the search space, by using the Fibonacci sequence to simulate graphs of rectangles with dimensions determined by consecutive pairs. Using the Kirchhoff matrix, we are able to find the number of spanning trees, and plot it in relation to vertices.
    Use the Fibonacci sequence as consecutive dimension pairs for rectangles.
    Write a function that finds the number of spanning trees of a given graph based on the Kirchhoff Matrix.
    Normalize the data for logarithmic plotting

    A Gallery of Unfolded Objects

    Below are depictions of 3D objects, their voxelized mesh, and crease pattern.

    3-Way Cube

    Couch

    Sphere

    Diamond

    Pyramid

    Conclusion

    This project develops a methodology for the design of the crease patterns for self-folding origami. The program developed a procedure for inputting a 3D object STL file and creating a voxelized mesh of the object . The algorithm calculates the efficiency of the generated crease pattern, with efficiency defined as the area of the minimum rectangle that can contain the entirety of the crease pattern. Generated crease patterns are verified to be valid through measuring square overlap and an animation allows the crease pattern to be simulated from partial to full folding. The algorithm demonstrates success with the unfolding of several soma cube and non-traditional shapes, for n < 10.
    ​

    Future Directions

    There are three main limitations to this work. Firstly, voxelization is an inherently lossy process, where objects, such as those that are curved, lose fine grained detail. The Hausdorff distance between the voxelized mesh and the original solid measures the worst-case surface deviation this introduces. Recovering finer detail requires a higher grid resolution n, but the number of faces grows rapidly, making the search more expensive.
    Second, a heuristic is needed to perform backtracking search of the tree. As compared to randomizing edge weights of the generated tree and exploring the design space unevenly, a backtracking search that can reuse information from failed attempts would raise both the success rate and speed.
    Finally, this work produces a valid crease pattern, but does not consider the folding procedure required to transition between folded and unfolded states. Generating a continuous motion path that avoids the sheet self-intersecting allows for an end-to-end self-folding origami pipeline. I hope to dedicate time using my project to explore the design space of biomedical nanostructures for therapeutic delivery.

    References

    mikolalysenko. (2026). GitHub - mikolalysenko/orthogami: Orthogonal polyhedra origami. GitHub. https://github.com/mikolalysenko/orthogami
    O’Rourke, J. (2008). Unfolding Orthogonal Polyhedra. Science.Smith.Edu. https://www.science.smith.edu/~jorourke/Papers/osurvey.pdf
    Damian, M., Demaine, E. D., & Flatland, R. (2011, December 20). Unfolding Orthogonal Polyhedra with Quadratic Refinement: The Delta-Unfolding Algorithm. https://arxiv.org/pdf/1112.4791

    Acknowledgements

    I would like to thank everyone part of organizing the Wolfram High School Summer Program including Eryn, Megan, Rory, and Stephen Wolfram for providing this opportunity to deepen my interests. I would also like to thank my mentor Dr. Anton Antonov for his support throughout the project, providing me valuable insight and asking me thought provoking questions throughout my progress during the project. I would finally like to thank my mentor group and TA group for making the entire program a memorable experience.
    Additional thanks to Stephen Wolfram for suggesting this project.

    CITE THIS NOTEBOOK

    A Computational approach to unfolding of orthogonal polyhedra​
    by Naren Pai​
    Wolfram Community, STAFF PICKS, July 9, 2026
    ​https://community.wolfram.com/groups/-/m/t/3750617