Foschi’s Folding Table Simulation

Nov. 27, 2022
This Mathematica notebook contains the code for simulating the rigid folding of the non-developable table created by Riccardo Foschi in 2022.  This is based on work from the paper “Explicit kinematic equations for degree-4 rigid origami vertices, Euclidean and non-Euclidean” by Riccardo Foschi, Thomas C. Hull, and Jason S. Ku (Physical Review E, Vol. 106, 2022, 055001).

First we will need matrix transformations in homogeneous coordinates:

Q[v_]:=Drop[v,None,-1]
RXh[t_]:={{1,0,0,0},{0,Cos[t],-Sin[t],0},{0,Sin[t],Cos[t],0},{0,0,0,1}}
RZh[t_]:={{Cos[t],-Sin[t],0,0},{Sin[t],Cos[t],0,0},{0,0,1,0},{0,0,0,1}}
T[{a_,b_}]:={{1,0,0,a},{0,1,0,b},{0,0,1,0},{0,0,0,1}}

Now we define the crease pattern faces.  This is a collection of polygons that will form the flexible polyhedral surface that is Foschi’s table. The polygons are defined in the xy-plane, but the vertices of the polyhedral surface are hyperbolic (i.e., non-developable with the sum of the sector angles around each vertex > 2π). This means that when we draw the initial places of all the polygon faces in the xy-plane, they will overlap.

cs=Sqrt[2]-1;
OT0={{cs,1,0,1},{1,cs,0,1},{1,-cs,0,1},{cs,-1,0,1},{-cs,-1,0,1},{-1,-cs,0,1},{-1,cs,0,1},{-cs,1,0,1}};
OT3={{cs,1,0,1},{cs+cs*Tan[22.5°],1+cs,0,1},{1+cs,cs+cs*Tan[22.5°],0,1},{1,cs,0,1}};
OT4={{cs,1,0,1},{cs+cs*Tan[22.5°],1+cs,0,1},{0,1+cs,0,1},{0,1,0,1}};
OT5={{cs,1,0,1},{cs,1+cs,0,1},{-cs,1+cs,0,1},{-cs,1,0,1}};
OT6={{1,cs,0,1},{1+cs,cs+cs*Tan[22.5°],0,1},{1+cs,0,0,1},{1,0,0,1}};
OT7={{1,cs,0,1},{1+cs,cs,0,1},{1+cs,-cs,0,1},{1,-cs,0,1}};
OT8={{1,0,0,1},{1+cs,0,0,1},{1+cs,-cs-cs*Tan[22.5°],0,1},{1,-cs,0,1}};
OT9={{1,-cs,0,1},{1+cs,-cs-cs*Tan[22.5°],0,1},{cs+cs*Tan[22.5°],-1-cs,0,1},{cs,-1,0,1}};
OT10={{cs,-1,0,1},{cs+cs*Tan[22.5°],-1-cs,0,1},{0,-1-cs,0,1},{0,-1,0,1}};
OT11={{cs,-1,0,1},{cs,-1-cs,0,1},{-cs,-1-cs,0,1},{-cs,-1,0,1}};
OT13={{-cs,-1,0,1},{-cs-cs*Tan[22.5°],-1-cs,0,1},{0,-1-cs,0,1},{0,-1,0,1}};
OT14={{-1,-cs,0,1},{-1-cs,-cs-cs*Tan[22.5°],0,1},{-cs-cs*Tan[22.5°],-1-cs,0,1},{-cs,-1,0,1}};
OT15={{-1,0,0,1},{-1-cs,0,0,1},{-1-cs,-cs-cs*Tan[22.5°],0,1},{-1,-cs,0,1}};
OT16={{-1,cs,0,1},{-1-cs,cs,0,1},{-1-cs,-cs,0,1},{-1,-cs,0,1}};
OT17={{-1,cs,0,1},{-1-cs,cs+cs*Tan[22.5°],0,1},{-1-cs,0,0,1},{-1,0,0,1}};
OT19={{-cs,1,0,1},{-cs-cs*Tan[22.5°],1+cs,0,1},{0,1+cs,0,1},{0,1,0,1}};
OT20={{-cs,1,0,1},{-cs-cs*Tan[22.5°],1+cs,0,1},{-1-cs,cs+cs*Tan[22.5°],0,1},{-1,cs,0,1}};
Graphics3D[Polygon[Q[#]]&/@{OT0,OT3,OT4,OT5,OT6,OT7,OT8,OT9,OT10,OT11,OT13,OT14,OT15,OT16,OT17,OT19,OT20}]

Next are the folding angle equations we’ll need for the non-developable vertices of the table. These equations are derived in our Physical Review E paper.

p1mode1[a_,b_,c_,d_,p4_]:=2ArcTan[(2 Sin[a] Sin[c] Tan[(p4/2)]+√(4 ((Sin[a])^2) ((Sin[c])^2) ((Tan[(p4/2)])^2)-(-Cos[b]+Cos[a-c-d]-Cos[b] ((Tan[(p4/2)])^2)+Cos[a+c-d] ((Tan[(p4/2)])^2)) (-Cos[b]+Cos[a+c+d]-Cos[b] ((Tan[(p4/2)])^2)+Cos[a-c+d] ((Tan[(p4/2)])^2))))/ (Cos[b]-Cos[a-c-d]+Cos[b] ((Tan[(p4/2)])^2)-Cos[a+c-d] ((Tan[(p4/2)])^2))]
p2mode1[a_,b_,c_,d_,p4_]:=-2ArcTan[((Sqrt[-(1+((Tan[p4/2])^2)) Cos[a+b]+((Tan[p4/2])^2) Cos[c-d]+Cos[c+d]])/(Sqrt[(1+((Tan[p4/2])^2)) Cos[a-b]-((Tan[p4/2])^2) Cos[c-d]-Cos[c+d]]))]

Now here is the simulation of the table folding.  The folding parameter t is the folding angle of the “legs” of the table, which start at t=0 (unfolded) and then can only fold as mountain creases if we assume that the table’s material cannot penetrate itself.  Therefore the t slider goes from 0 to –π.

Manipulate[Module[{a=5π/8,b=5π/8,c=π/2,d=3π/4,
v1={cs,1},v2={1,cs},v3={1,-cs},v4={cs,-1},v5={-cs,-1},v6={-1,-cs},v7={-1,cs},v8={-cs,1}},
Graphics3D[{EdgeForm[Thick],GrayLevel[.25],Specularity[White,50],Lighting->PointLight[Gray,{0,0,2}],
Polygon[Q[OT0]],
Polygon[Q[T[v1].RZh[-(π-d)].RXh[p1mode1[a,b,c,d,t]].RZh[π-d].T[-v1].#&/@OT3]],
Polygon[Q[T[v1].RZh[-(π-d)].RXh[p1mode1[a,b,c,d,t]].RZh[π-d].RZh[-(-a+π-d)].RXh[p2mode1[a,b,c,d,t]].RZh[-a+π-d].T[-v1].#&/@OT4]],
Polygon[Q[T[v1].RZh[-π].RXh[-t].RZh[π].T[-v1].#&/@OT5]],
Polygon[Q[T[v2].RZh[d].RXh[-p1mode1[a,b,c,d,t]].RZh[-d].RZh[a-π/2].RXh[-p2mode1[a,b,c,d,t]].RZh[-(a-π/2)].T[-v2].#&/@OT6]],
Polygon[Q[T[v2].RZh[-π/2].RXh[t].RZh[π/2].T[-v2].#&/@OT7]],
Polygon[Q[T[v3].RZh[d+π/2].RXh[p1mode1[a,b,c,d,t]].RZh[-(d+π/2)].RZh[-(a-π/2)].RXh[p2mode1[a,b,c,d,t]].RZh[a-π/2].T[-v3].#&/@OT8]],
Polygon[Q[T[v3].RZh[d+π/2].RXh[p1mode1[a,b,c,d,t]].RZh[-(d+π/2)].T[-v3].#&/@OT9]],
Polygon[Q[T[v3].RZh[d+π/2].RXh[p1mode1[a,b,c,d,t]].RZh[-(d+π/2)].T[-v3].T[v4].RZh[-(π-a)].RXh[-p2mode1[a,b,c,d,t]].RZh[π-a].T[-v4].#&/@OT10]],
Polygon[Q[T[v4].RZh[-π].RXh[t].RZh[π].T[-v4].#&/@OT11]],
Polygon[Q[T[v5].RZh[d].RXh[p1mode1[a,b,c,d,t]].RZh[-d].RZh[a+d].RXh[p2mode1[a,b,c,d,t]].RZh[-(a+d)].T[-v5].#&/@OT13]],
Polygon[Q[T[v5].RZh[d].RXh[p1mode1[a,b,c,d,t]].RZh[-d].T[-v5].#&/@OT14]],
Polygon[Q[T[v6].RZh[d].RXh[p1mode1[a,b,c,d,t]].RZh[-d].RZh[a-π/2].RXh[p2mode1[a,b,c,d,t]].RZh[-(a-π/2)].T[-v6].#&/@OT15]],
Polygon[Q[T[v7].RZh[-π/2].RXh[-t].RZh[π/2].T[-v7].#&/@OT16]],
Polygon[Q[T[v7].RZh[-d].RXh[-p1mode1[a,b,c,d,t]].RZh[d].RZh[-(a-π/2)].RXh[-p2mode1[a,b,c,d,t]].RZh[a-π/2].T[-v7].#&/@OT17]],
Polygon[Q[T[v8].RZh[π-d].RXh[p1mode1[a,b,c,d,t]].RZh[-(π-d)].T[-v8].#&/@OT20]],
Polygon[Q[T[v8].RZh[π-d].RXh[p1mode1[a,b,c,d,t]].RZh[-(π-d)].RZh[-a+π-d].RXh[p2mode1[a,b,c,d,t]].RZh[-(-a+π-d)].T[-v8].#&/@OT19]]
},
PlotRange->{{-1.6,1.6},{-1.6,1.6},{-1,1}},Lighting->"Neutral",Boxed->False]
],
{{t,-0.001},-π,-0.001}]