2323 * Modified by Jim Huang <[email protected] > 2424 * - Refine the comments 
2525 * - Colorize the renderer 
26-  * - Support nanosleep  
26+  * - Adapt ANSI graphical enhancements from Bruno Levy  
2727 */ 
2828
2929/* An ASCII donut renderer that relies solely on shifts, additions, 
3535#include  <stdint.h> 
3636#include  <stdio.h> 
3737#include  <string.h> 
38- #include  <time.h> 
38+ #if  !defined(__riscv )
39+ #include  <unistd.h> 
40+ #endif 
3941
40- /* 0 for 80x24, 1 for 160x48, etc. */ 
41- enum  {
42-     RESX_SHIFT  =  0 ,
43-     RESY_SHIFT  =  0 ,
42+ /* Define 1 for a more accurate result (but it costs a bit) */ 
43+ #define  PRECISE  0
44+ 
45+ #define  USE_MULTIPLIER  1
46+ 
47+ static  const  char  * colormap [34 ] =  {
48+     "0" ,       "8;5;232" , "8;5;233" , "8;5;234" , "8;5;235" , "8;5;236" , "8;5;237" ,
49+     "8;5;238" , "8;5;239" , "8;5;240" , "8;5;241" , "8;5;242" , "8;5;243" , "8;5;244" ,
50+     "8;5;245" , "8;5;246" , "8;5;247" , "8;5;248" , "8;5;249" , "8;5;250" , "8;5;251" ,
51+     "8;5;252" , "8;5;253" , "8;5;254" , "8;5;255" , "7" ,       "8;5;16" ,  "8;5;17" ,
52+     "8;5;18" ,  "8;5;19" ,  "8;5;20" ,  "8;5;21" ,  "8;5;22" ,  "8;5;23" ,
4453};
4554
55+ /* Previous background/foreground colors */ 
56+ static  int  prev_color1  =  0 , prev_color2  =  0 ;
57+ 
58+ static  inline  void  setcolors (int  fg  /* foreground */ , int  bg  /* background */ )
59+ {
60+     printf ("\033[4%s;3%sm" , colormap [bg ], colormap [fg ]);
61+ }
62+ 
63+ static  inline  void  setpixel (int  x , int  y , int  color )
64+ {
65+     /* Stash the "upper" scanline so we can combine two rows of output. */ 
66+     static  char  scanline [80 ];
67+     int  c1 , c2 ;
68+ 
69+     /* On even row (y & 1 == 0), just remember the color; no output yet. */ 
70+     if  (!(y  &  1 )) {
71+         scanline [x ] =  color ;
72+         return ;
73+     }
74+ 
75+     /* On the odd row, pull the stored color from the previous row. */ 
76+     c1  =  scanline [x ]; /* background */ 
77+     c2  =  color ;       /* foreground */ 
78+ 
79+     /* Same background/foreground: print a space with only background color */ 
80+     if  (c1  ==  c2 ) {
81+         if  (prev_color1  !=  c1 ) {
82+             printf ("\033[4%sm " , colormap [c1 ]);
83+             prev_color1  =  c1 ;
84+         } else  { /* Already set, just print a space */ 
85+             putchar (' ' );
86+         }
87+         return ;
88+     }
89+ 
90+     /* Different colors: print a block with new bg/fg if either changed */ 
91+     if  (prev_color1  !=  c1  ||  prev_color2  !=  c2 ) {
92+         printf ("\033[4%s;3%sm" , colormap [c1 ], colormap [c2 ]);
93+         prev_color1  =  c1 ;
94+         prev_color2  =  c2 ;
95+     }
96+     printf ("\u2583" );
97+ }
98+ 
4699/* Torus radius and camera distance. 
47100 * These values are closely tied to other constants, so modifying them 
48101 * significantly may lead to unexpected behavior. 
@@ -60,6 +113,12 @@ static const int dz = 5, r1 = 1, r2 = 2;
60113    x -= (y >> s); \
61114    y += (x >> s)
62115
116+ #if  PRECISE 
117+ #define  N_CORDIC  10
118+ #else 
119+ #define  N_CORDIC  6
120+ #endif 
121+ 
63122/* CORDIC algorithm used to calculate the magnitude of the vector |x, y| by 
64123 * rotating the vector onto the x-axis. This operation also transforms vector 
65124 * (x2, y2) accordingly, and updates the value of x2. This rotation is employed 
@@ -68,87 +127,98 @@ static const int dz = 5, r1 = 1, r2 = 2;
68127 * noting that only one of the two lighting normal coordinates needs to be 
69128 * retained. 
70129 */ 
71- #define  N_CORDIC  6
72130static  int  length_cordic (int16_t  x , int16_t  y , int16_t  * x2_ , int16_t  y2 )
73131{
74132    int  x2  =  * x2_ ;
75-     if  (x  <  0 ) {  // start in right half-plane 
133+ 
134+     /* Move x into the right half-plane */ 
135+     if  (x  <  0 ) {
76136        x  =  - x ;
77137        x2  =  - x2 ;
78138    }
139+ 
140+     /* CORDIC iterations */ 
79141    for  (int  i  =  0 ; i  <  N_CORDIC ; i ++ ) {
80-         int  t  =  x ;
81-         int  t2  =  x2 ;
82-         if  (y  <  0 ) {
83-             x  -=  y  >> i ;
84-             y  +=  t  >> i ;
85-             x2  -=  y2  >> i ;
86-             y2  +=  t2  >> i ;
87-         } else  {
88-             x  +=  y  >> i ;
89-             y  -=  t  >> i ;
90-             x2  +=  y2  >> i ;
91-             y2  -=  t2  >> i ;
92-         }
142+         int  t  =  x , t2  =  x2 ;
143+         int  sign  =  (y  <  0 ) ? -1  : 1 ;
144+ 
145+         x  +=  sign  *  (y  >> i );
146+         y  -=  sign  *  (t  >> i );
147+         x2  +=  sign  *  (y2  >> i );
148+         y2  -=  sign  *  (t2  >> i );
93149    }
94-     /* Divide by 0.625 as a rough approximation to the 0.607 scaling factor 
95-      * introduced by this algorithm 
96-      * See https://en.wikipedia.org/wiki/CORDIC 
150+ 
151+     /* Divide by ~0.625 (5/8) to approximate the 0.607 scaling factor 
152+      * introduced by the CORDIC algorithm. See: 
153+      * https://en.wikipedia.org/wiki/CORDIC 
97154     */ 
98-     * x2_  =  (x2  >> 1 ) +  (x2  >> 3 ) -  (x2  >> 6 );
155+     * x2_  =  (x2  >> 1 ) +  (x2  >> 3 );
156+ #if  PRECISE 
157+     * x2_  -=  x2  >> 6 ; /* get closer to 0.607 */ 
158+ #endif 
159+ 
99160    return  (x  >> 1 ) +  (x  >> 3 ) -  (x  >> 6 );
100161}
101162
102163int  main ()
103164{
165+     printf (
166+         "\033[48;5;16m"  /* set background color black */ 
167+         "\033[38;5;15m"  /* set foreground color white */ 
168+         "\033[H"         /* move cursor home */ 
169+         "\033[?25l"      /* hide cursor */ 
170+         "\033[2J" );     /* clear screen */ 
171+ 
172+     int  frame  =  1 ;
173+ 
104174    /* Precise rotation directions, sines, cosines, and their products */ 
105175    int16_t  sB  =  0 , cB  =  16384 ;
106176    int16_t  sA  =  11583 , cA  =  11583 ;
107177    int16_t  sAsB  =  0 , cAsB  =  0 ;
108178    int16_t  sAcB  =  11583 , cAcB  =  11583 ;
109179
110180    for  (int  count  =  0 ; count  <  500 ; count ++ ) {
111-         /* This is a multiplication, but since dz is 5, it is equivalent to 
181+         /* Starting position (p0). 
182+          * This is a multiplication, but since dz is 5, it is equivalent to 
112183         * (sb + (sb << 2)) >> 6. 
113184         */ 
114185        const  int16_t  p0x  =  dz  *  sB  >> 6 ;
115186        const  int16_t  p0y  =  dz  *  sAcB  >> 6 ;
116187        const  int16_t  p0z  =  - dz  *  cAcB  >> 6 ;
117188
118-         const  int  r1i  =  r1  *  256 ;
119-         const  int  r2i  =  r2  *  256 ;
189+         const  int  r1i  =  r1  *  256 , r2i  =  r2  *  256 ;
190+ 
191+         int  n_iters  =  0 ;
120192
121-         int  niters  =  0 ;
122-         int  nnormals  =  0 ;
123193        /* per-row increments 
124194         * These can all be compiled into two shifts and an add. 
125195         */ 
126-         int16_t  yincC  =  (12   *   cA )  >> ( 8  +  RESY_SHIFT ); 
127-         int16_t  yincS  =  (12   *   sA )  >> ( 8  +  RESY_SHIFT ); 
196+         int16_t  yincC  =  (cA  >> 6 )  +  ( cA  >>  5 );  /* 12*cA >> 8 */ 
197+         int16_t  yincS  =  (sA  >> 6 )  +  ( sA  >>  5 );  /* 12*sA >> 8 */ 
128198
129199        /* per-column increments */ 
130-         int16_t  xincX  =  (6   *   cB )  >> ( 8   +   RESX_SHIFT ); 
131-         int16_t  xincY  =  (6   *   sAsB )  >> ( 8   +   RESX_SHIFT ); 
132-         int16_t  xincZ  =  (6   *   cAsB )  >> ( 8   +   RESX_SHIFT ); 
200+         int16_t  xincX  =  (cB  >>  7 )  +  ( cB  >>  6 );      /* 6*cB  >> 8 */ 
201+         int16_t  xincY  =  (sAsB  >>  7 )  +  ( sAsB  >>  6 );  /* 6* sAsB >> 8 */ 
202+         int16_t  xincZ  =  (cAsB  >>  7 )  +  ( cAsB  >>  6 );  /* 6* cAsB >> 8 */ 
133203
134204        /* top row y cosine/sine */ 
135-         int16_t  ycA  =  - ((cA  >> 1 ) +  (cA  >> 4 ));   //  -12 * yinc1 = -9*cA >> 4; 
136-         int16_t  ysA  =  - ((sA  >> 1 ) +  (sA  >> 4 ));   //  -12 * yinc2 = -9*sA >> 4; 
205+         int16_t  ycA  =  - ((cA  >> 1 ) +  (cA  >> 4 )); /*  -12 * yinc1 = -9*cA >> 4 */ 
206+         int16_t  ysA  =  - ((sA  >> 1 ) +  (sA  >> 4 )); /*  -12 * yinc2 = -9*sA >> 4 */ 
137207
138-         for  (int  j  =  0 ; j  <  (24  << RESY_SHIFT ) -  1 ;
139-              j ++ , ycA  +=  yincC , ysA  +=  yincS ) {
140-             /* left columnn x cosines/sines */ 
141-             int  xsAsB  =  (sAsB  >> 4 ) -  sAsB ;  // -40 * xincY 
142-             int  xcAsB  =  (cAsB  >> 4 ) -  cAsB ;  // -40 * xincZ; 
208+         int  xsAsB  =  (sAsB  >> 4 ) -  sAsB ; /* -40*xincY */ 
209+         int  xcAsB  =  (cAsB  >> 4 ) -  cAsB ; /* -40*xincZ */ 
143210
211+         /* Render rows */ 
212+         for  (int  j  =  0 ; j  <  46 ; j ++ , ycA  +=  yincC  >> 1 , ysA  +=  yincS  >> 1 ) {
144213            /* ray direction */ 
145-             int16_t  vxi14  =  (cB  >> 4 ) -  cB  -  sB ;  // -40 *  xincX - sB; 
146-             int16_t  vyi14  =  ( ycA  -  xsAsB  -  sAcB ) ;
147-             int16_t  vzi14  =  ( ysA  +  xcAsB  +  cAcB ) ;
214+             int16_t  vxi14  =  (cB  >> 4 ) -  cB  -  sB ;  // -40* xincX - sB; 
215+             int16_t  vyi14  =  ycA  -  xsAsB  -  sAcB ;
216+             int16_t  vzi14  =  ysA  +  xcAsB  +  cAcB ;
148217
149-             for  (int  i  =  0 ; i  <  ((80  << RESX_SHIFT ) -  1 );
218+             /* Render columns */ 
219+             for  (int  i  =  0 ; i  <  79 ;
150220                 i ++ , vxi14  +=  xincX , vyi14  -=  xincY , vzi14  +=  xincZ ) {
151-                 int  t  =  512 ;   //  (256 * dz) - r2i - r1i; 
221+                 int  t  =  512 ; /* Depth accumulation:  (256 * dz) - r2i - r1i */ 
152222
153223                /* Assume t = 512, t * vxi >> 8 == vxi << 1 */ 
154224                int16_t  px  =  p0x  +  (vxi14  >> 5 );
@@ -158,6 +228,7 @@ int main()
158228                int16_t  ly0  =  (sAcB  -  cA ) >> 2 ;
159229                int16_t  lz0  =  (- cAcB  -  sA ) >> 2 ;
160230                for  (;;) {
231+                     /* Distance from torus surface */ 
161232                    int  t0 , t1 , t2 , d ;
162233                    int16_t  lx  =  lx0 , ly  =  ly0 , lz  =  lz0 ;
163234                    t0  =  length_cordic (px , py , & lx , ly );
@@ -166,26 +237,27 @@ int main()
166237                    d  =  t2  -  r1i ;
167238                    t  +=  d ;
168239
169-                     if  (t  >  8  *  256 ) {
170-                         putchar (' ' );
240+                     if  (t  >  (8  *  256 )) {
241+                         int  N  =  (((j  -  frame ) >> 3 ) ^ (((i  +  frame ) >> 3 ))) &  1 ;
242+                         setpixel (i , j , (N  << 2 ) +  26 );
171243                        break ;
172-                     } else  if  (d  <  2 ) {
173-                         int  N  =  lz  >> 9 ;
174-                         static  const  char  charset [] =  ".,-~:;!*=#$@" ;
175-                         printf ("\033[48;05;%dm%c\033[0m" , N  / 4  +  1 ,
176-                                charset [N  >  0  ? N  <  12  ? N  : 11  : 0 ]);
177-                         nnormals ++ ;
244+                     }
245+                     if  (d  <  2 ) {
246+                         int  N  =  lz  >> 8 ;
247+                         N  =  N  >  0  ? N  <  26  ? N  : 25  : 0 ;
248+                         setpixel (i , j , N );
178249                        break ;
179250                    }
180251
252+ #ifdef  USE_MULTIPLIER 
253+                     px  +=  d  *  vxi14  >> 14 ;
254+                     py  +=  d  *  vyi14  >> 14 ;
255+                     pz  +=  d  *  vzi14  >> 14 ;
256+ #else 
181257                    {
182-                         /* equivalent to: 
183-                          * px += d*vxi14 >> 14; 
184-                          * py += d*vyi14 >> 14; 
185-                          * pz += d*vzi14 >> 14; 
186-                          * 
187-                          * idea is to make a 3d vector mul hw peripheral 
188-                          * equivalent to this algorithm 
258+                         /* Using a 3D fixed-point partial multiply approach, the 
259+                          * idea is to make a 3D vector multiplication hardware 
260+                          * peripheral equivalent to this algorithm. 
189261                         */ 
190262
191263                        /* 11x1.14 fixed point 3x parallel multiply only 16 bit 
@@ -196,14 +268,10 @@ int main()
196268                        int16_t  a  =  vxi14 , b  =  vyi14 , c  =  vzi14 ;
197269                        while  (d ) {
198270                            if  (d  &  1024 ) {
199-                                 dx  +=  a ;
200-                                 dy  +=  b ;
201-                                 dz  +=  c ;
271+                                 dx  +=  a , dy  +=  b , dz  +=  c ;
202272                            }
203273                            d  =  (d  &  1023 ) << 1 ;
204-                             a  >>= 1 ;
205-                             b  >>= 1 ;
206-                             c  >>= 1 ;
274+                             a  >>= 1 , b  >>= 1 , c  >>= 1 ;
207275                        }
208276                        /* We have already shifted down by 10 bits, so this 
209277                         * extracts the last four bits. 
@@ -212,14 +280,23 @@ int main()
212280                        py  +=  dy  >> 4 ;
213281                        pz  +=  dz  >> 4 ;
214282                    }
283+ #endif 
215284
216-                     niters ++ ;
285+                     n_iters ++ ;
217286                }
218287            }
219-             puts ("" );
288+             if  (j  &  1 )
289+                 printf ("\r\n" );
220290        }
221-         printf ("%d iterations %d lit pixels\x1b[K" , niters , nnormals );
291+ 
292+         setcolors (25 , 33 );
293+         printf ("%6d iterations" , n_iters );
294+         setcolors (25 , 0 );
295+         printf ("\x1b[K" );
296+ 
297+ #if  !defined(__riscv )
222298        fflush (stdout );
299+ #endif 
223300
224301        /* Rotate sines, cosines, and their products to animate the torus 
225302         * rotation about two axes. 
@@ -231,10 +308,16 @@ int main()
231308        R (6 , cAcB , cAsB );
232309        R (6 , sAcB , sAsB );
233310
234-          /* FIXME: Adjust tv_nsec to align with runtime expectations. */ 
235-         struct   timespec   ts   =  {. tv_sec   =   0 , . tv_nsec   =   30000 } ;
236-          nanosleep ( & ts ,  & ts ); 
311+ #if  !defined( __riscv ) 
312+         usleep ( 15000 ) ;
313+ #endif 
237314
238-         printf ("\r\x1b[%dA" , (24  << RESY_SHIFT ) -  1 );
315+         printf ("\r\x1b[23A" );
316+         ++ frame ;
317+         prev_color1  =  prev_color2  =  -1 ;
239318    }
319+ 
320+     /* Show cursor again */ 
321+     printf ("\033[?25h" );
322+     return  0 ;
240323}
0 commit comments