This work presents a code translation of Andy Sloane's fixed-point donut.c into Wolfram Language, utilizing the FunctionCompile feature for efficient computation. The project demonstrates how low-level C algorithms can be faithfully reproduced in a high-level symbolic environment. By combining Style and ListAnimate, the implementation enables smooth ASCII-based animation with customizable color effects.
In[]:=
Clear[RotateStep,IntDonutFrame];
Create a compiler environment to store the declarations of the functions:
In[]:=
env=CreateCompilerEnvironment[];
CORDIC + Newton step to keep precision for repeated computation:
In[]:=
decl1=FunctionDeclaration[RotateStep,Typed[​​{"PackedArray"::["MachineInteger",1],"MachineInteger","MachineInteger"}​​->"PackedArray"::["MachineInteger",1]​​]@Function[​​{arg1,mul,shift},​​Module[{xx=arg1[[1]],yy=arg1[[2]],tmp,star},tmp=xx;​​xx=xx-BitShiftRight[mul*yy,shift];​​yy=yy+BitShiftRight[mul*tmp,shift];​​star=BitShiftRight[3145728-xx*xx-yy*yy,11];​​xx=BitShiftRight[xx*star,10];​​yy=BitShiftRight[yy*star,10];​​{xx,yy}]​​]];
Declare the core function that handles rotation and ASCII rendering:
In[]:=
decl2=FunctionDeclaration[IntDonutFrame,Typed[​​{"MachineInteger","MachineInteger","MachineInteger","MachineInteger"}->"ListVector"::["String"]​​]​​@Function[{cA,sA,cB,sB},​​Module[{w=80,h=22,​​chars={".",",","-","~",":",";","=","!","*","#","$","@"},​​b=CreateDataStructure["FixedArray"," ",1760],​​z=CreateDataStructure["FixedArray",127,1760],​​sj=0,cj=1024,si=0,ci=0,​​R1=1,R2=2048,K2=5120*1024,​​x0,x1,x2,x3,x4,x5,x6,x7,x,y,Nl=1,o=0,zz},​​Do[si=0;ci=1024;​​Do[x0=R1*cj+R2;​​x1=BitShiftRight[ci*x0,10];​​x2=BitShiftRight[cA*sj,10];​​x3=BitShiftRight[si*x0,10];​​x4=R1*x2-BitShiftRight[sA*x3,10];​​x5=BitShiftRight[sA*sj,10];​​x6=K2+R1*1024*x5+cA*x3;​​x7=BitShiftRight[cj*si,10];​​x=40+Quotient[30*(cB*x1-sB*x4),x6];​​y=12+Quotient[15*(cB*x4+sB*x1),x6];​​Nl=BitShiftRight[-cA*x7-cB*(BitShiftRight[-sA*x7,10]+x2)-ci*BitShiftRight[cj*sB,10],10];​​Nl=BitShiftRight[Nl-x5,7];​​o=x+80*y;​​zz=BitShiftRight[x6-K2,15];​​If[0<y<22&&0<x<80&&zz<z["Part",o+1],​​z["SetPart",o+1,zz];​​b["SetPart",o+1,chars[[If[Nl>0,Nl+1,1]]]]​​];​​{ci,si}=RotateStep[{ci,si},5,8];,{i,0,323,1}];​​{cj,sj}=RotateStep[{cj,sj},9,7];,{j,0,89,1}];​​b["Elements"]​​]]];
Put two declarations into one environment:
In[]:=
CompilerEnvironmentAppendTo[env,{decl1,decl2}]
Out[]=
CompilerEnvironmentObject
Target systems:
MacOSX-ARM64
User types:
0
User functions:
2

Compile the rotation function and core function:
In[]:=
cfRotateStep=FunctionCompile[Function[{​​Typed[arg1,"PackedArray"::["MachineInteger",1]],​​Typed[m,"MachineInteger"],​​Typed[s,"MachineInteger"]​​}​​,RotateStep[arg1,m,s]],CompilerEnvironment->env];
In[]:=
Information@cfRotateStep
Out[]=
Compiled Code Function
Argument Types
{PackedArray::[Integer64,1],Integer64,Integer64}
Return Type
PackedArray::[Integer64,1]
Type
{PackedArray::[Integer64,1],Integer64,Integer64}PackedArray::[Integer64,1]
LLVM Binary
MacOSX-ARM64ByteArray[<10336>]
In[]:=
cfIntDonutFrame=FunctionCompile[Function[{​​Typed[cA,"MachineInteger"],​​Typed[sA,"MachineInteger"],​​Typed[cB,"MachineInteger"],​​Typed[sB,"MachineInteger"]​​}​​,IntDonutFrame[cA,sA,cB,sB]],CompilerEnvironment->env];
The summary of the internal structure of the function:
In[]:=
Information@cfIntDonutFrame
Out[]=
Compiled Code Function
Argument Types
{Integer64,Integer64,Integer64,Integer64}
Return Type
ListVector::[String]
Type
{Integer64,Integer64,Integer64,Integer64}ListVector::[String]
LLVM Binary
MacOSX-ARM64ByteArray[<48656>]
Use the compiled function cfRotateStep to create a list with 600 sets of orientation for the 3-D donut:
In[]:=
configs=Join@@@Transpose@{NestList[cfRotateStep[#,5,7]&,{0,1024},600],​​NestList[cfRotateStep[#,5,7]&,{1024,0},600]};
Distribute the definitions of my functions onto subkernels:
In[]:=
Once[LaunchKernels[];DistributeDefinitions[cfRotateStep,cfIntDonutFrame]];
On my Mac air M2, it takes less than 2 seconds to generate all 600 frames on 8 parallel subkernels:
In[]:=
AbsoluteTiming[data=ParallelMap[StringJoin/@Partition[cfIntDonutFrame[Sequence@@#],80]&,configs];]
Out[]=
{1.02595,Null}
Customized the color scheme used in the example:
In[]:=
(*hbrw=RGBColor[107/256,218/256,70/256];tomatchcolorschemeinmactermimalhomebrewcolor*)​​clb=Blend[{White,RGBColor["#9B2A7F"],RGBColor["#748358"],RGBColor["#57C78B"],RGBColor["#E647AE"],White},#]&;
Run animation with other settings in Style function to adjust size. Highly recommend to run the code in the dark mode:
In[]:=
ListAnimate[Table[​​Style[Column[data[[i]],Spacings->0.2],Bold,​​FontFamily->"Courier",FontSize->12,clb[i/600](*hbrw*)],{i,600}],60,​​SaveDefinitions->True]
Out[]=
The compiled version is roughly 100 times faster than the uncompiled version.

CITE THIS NOTEBOOK

Reconstructing the classic ASCII Donut in Wolfram language using FunctionCompile​
by Shenghui Yang​
Wolfram Community, STAFF PICKS, October 24, 2025
​https://community.wolfram.com/groups/-/m/t/3564927