23
23
* Modified by Jim Huang <[email protected] >
24
24
* - Refine the comments
25
25
* - Colorize the renderer
26
- * - Support nanosleep
26
+ * - Adapt ANSI graphical enhancements from Bruno Levy
27
27
*/
28
28
29
29
/* An ASCII donut renderer that relies solely on shifts, additions,
35
35
#include <stdint.h>
36
36
#include <stdio.h>
37
37
#include <string.h>
38
- #include <time.h>
38
+ #if !defined(__riscv )
39
+ #include <unistd.h>
40
+ #endif
39
41
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
+ static const char * colormap [34 ] = {
46
+ "0" , "8;5;232" , "8;5;233" , "8;5;234" , "8;5;235" , "8;5;236" , "8;5;237" ,
47
+ "8;5;238" , "8;5;239" , "8;5;240" , "8;5;241" , "8;5;242" , "8;5;243" , "8;5;244" ,
48
+ "8;5;245" , "8;5;246" , "8;5;247" , "8;5;248" , "8;5;249" , "8;5;250" , "8;5;251" ,
49
+ "8;5;252" , "8;5;253" , "8;5;254" , "8;5;255" , "7" , "8;5;16" , "8;5;17" ,
50
+ "8;5;18" , "8;5;19" , "8;5;20" , "8;5;21" , "8;5;22" , "8;5;23" ,
44
51
};
45
52
53
+ /* Previous background/foreground colors */
54
+ static int prev_color1 = 0 , prev_color2 = 0 ;
55
+
56
+ static inline void setcolors (int fg /* foreground */ , int bg /* background */ )
57
+ {
58
+ printf ("\033[4%s;3%sm" , colormap [bg ], colormap [fg ]);
59
+ }
60
+
61
+ static inline void setpixel (int x , int y , int color )
62
+ {
63
+ /* Stash the "upper" scanline so we can combine two rows of output. */
64
+ static char scanline [80 ];
65
+ int c1 , c2 ;
66
+
67
+ /* On even row (y & 1 == 0), just remember the color; no output yet. */
68
+ if (!(y & 1 )) {
69
+ scanline [x ] = color ;
70
+ return ;
71
+ }
72
+
73
+ /* On the odd row, pull the stored color from the previous row. */
74
+ c1 = scanline [x ]; /* background */
75
+ c2 = color ; /* foreground */
76
+
77
+ /* Same background/foreground: print a space with only background color */
78
+ if (c1 == c2 ) {
79
+ if (prev_color1 != c1 ) {
80
+ printf ("\033[4%sm " , colormap [c1 ]);
81
+ prev_color1 = c1 ;
82
+ } else { /* Already set, just print a space */
83
+ putchar (' ' );
84
+ }
85
+ return ;
86
+ }
87
+
88
+ /* Different colors: print a block with new bg/fg if either changed */
89
+ if (prev_color1 != c1 || prev_color2 != c2 ) {
90
+ printf ("\033[4%s;3%sm" , colormap [c1 ], colormap [c2 ]);
91
+ prev_color1 = c1 ;
92
+ prev_color2 = c2 ;
93
+ }
94
+ printf ("\u2583" );
95
+ }
96
+
46
97
/* Torus radius and camera distance.
47
98
* These values are closely tied to other constants, so modifying them
48
99
* significantly may lead to unexpected behavior.
@@ -60,6 +111,12 @@ static const int dz = 5, r1 = 1, r2 = 2;
60
111
x -= (y >> s); \
61
112
y += (x >> s)
62
113
114
+ #if PRECISE
115
+ #define N_CORDIC 10
116
+ #else
117
+ #define N_CORDIC 6
118
+ #endif
119
+
63
120
/* CORDIC algorithm used to calculate the magnitude of the vector |x, y| by
64
121
* rotating the vector onto the x-axis. This operation also transforms vector
65
122
* (x2, y2) accordingly, and updates the value of x2. This rotation is employed
@@ -68,87 +125,98 @@ static const int dz = 5, r1 = 1, r2 = 2;
68
125
* noting that only one of the two lighting normal coordinates needs to be
69
126
* retained.
70
127
*/
71
- #define N_CORDIC 6
72
128
static int length_cordic (int16_t x , int16_t y , int16_t * x2_ , int16_t y2 )
73
129
{
74
130
int x2 = * x2_ ;
75
- if (x < 0 ) { // start in right half-plane
131
+
132
+ /* Move x into the right half-plane */
133
+ if (x < 0 ) {
76
134
x = - x ;
77
135
x2 = - x2 ;
78
136
}
137
+
138
+ /* CORDIC iterations */
79
139
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
- }
140
+ int t = x , t2 = x2 ;
141
+ int sign = (y < 0 ) ? -1 : 1 ;
142
+
143
+ x += sign * (y >> i );
144
+ y -= sign * (t >> i );
145
+ x2 += sign * (y2 >> i );
146
+ y2 -= sign * (t2 >> i );
93
147
}
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
148
+
149
+ /* Divide by ~0.625 (5/8) to approximate the 0.607 scaling factor
150
+ * introduced by the CORDIC algorithm. See:
151
+ * https://en.wikipedia.org/wiki/CORDIC
97
152
*/
98
- * x2_ = (x2 >> 1 ) + (x2 >> 3 ) - (x2 >> 6 );
153
+ * x2_ = (x2 >> 1 ) + (x2 >> 3 );
154
+ #if PRECISE
155
+ * x2_ -= x2 >> 6 ; /* get closer to 0.607 */
156
+ #endif
157
+
99
158
return (x >> 1 ) + (x >> 3 ) - (x >> 6 );
100
159
}
101
160
102
161
int main ()
103
162
{
163
+ printf (
164
+ "\033[48;5;16m" /* set background color black */
165
+ "\033[38;5;15m" /* set foreground color white */
166
+ "\033[H" /* move cursor home */
167
+ "\033[?25l" /* hide cursor */
168
+ "\033[2J" ); /* clear screen */
169
+
170
+ int frame = 1 ;
171
+
104
172
/* Precise rotation directions, sines, cosines, and their products */
105
173
int16_t sB = 0 , cB = 16384 ;
106
174
int16_t sA = 11583 , cA = 11583 ;
107
175
int16_t sAsB = 0 , cAsB = 0 ;
108
176
int16_t sAcB = 11583 , cAcB = 11583 ;
109
177
110
178
for (int count = 0 ; count < 500 ; count ++ ) {
111
- /* This is a multiplication, but since dz is 5, it is equivalent to
179
+ /* Starting position (p0).
180
+ * This is a multiplication, but since dz is 5, it is equivalent to
112
181
* (sb + (sb << 2)) >> 6.
113
182
*/
114
183
const int16_t p0x = dz * sB >> 6 ;
115
184
const int16_t p0y = dz * sAcB >> 6 ;
116
185
const int16_t p0z = - dz * cAcB >> 6 ;
117
186
118
- const int r1i = r1 * 256 ;
119
- const int r2i = r2 * 256 ;
187
+ const int r1i = r1 * 256 , r2i = r2 * 256 ;
188
+
189
+ int n_iters = 0 ;
120
190
121
- int niters = 0 ;
122
- int nnormals = 0 ;
123
191
/* per-row increments
124
192
* These can all be compiled into two shifts and an add.
125
193
*/
126
- int16_t yincC = (12 * cA ) >> ( 8 + RESY_SHIFT );
127
- int16_t yincS = (12 * sA ) >> ( 8 + RESY_SHIFT );
194
+ int16_t yincC = (cA >> 6 ) + ( cA >> 5 ); /* 12*cA >> 8 */
195
+ int16_t yincS = (sA >> 6 ) + ( sA >> 5 ); /* 12*sA >> 8 */
128
196
129
197
/* 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 );
198
+ int16_t xincX = (cB >> 7 ) + ( cB >> 6 ); /* 6*cB >> 8 */
199
+ int16_t xincY = (sAsB >> 7 ) + ( sAsB >> 6 ); /* 6* sAsB >> 8 */
200
+ int16_t xincZ = (cAsB >> 7 ) + ( cAsB >> 6 ); /* 6* cAsB >> 8 */
133
201
134
202
/* 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;
203
+ int16_t ycA = - ((cA >> 1 ) + (cA >> 4 )); /* -12 * yinc1 = -9*cA >> 4 */
204
+ int16_t ysA = - ((sA >> 1 ) + (sA >> 4 )); /* -12 * yinc2 = -9*sA >> 4 */
137
205
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;
206
+ int xsAsB = (sAsB >> 4 ) - sAsB ; /* -40*xincY */
207
+ int xcAsB = (cAsB >> 4 ) - cAsB ; /* -40*xincZ */
143
208
209
+ /* Render rows */
210
+ for (int j = 0 ; j < 46 ; j ++ , ycA += yincC >> 1 , ysA += yincS >> 1 ) {
144
211
/* 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 ) ;
212
+ int16_t vxi14 = (cB >> 4 ) - cB - sB ; // -40* xincX - sB;
213
+ int16_t vyi14 = ycA - xsAsB - sAcB ;
214
+ int16_t vzi14 = ysA + xcAsB + cAcB ;
148
215
149
- for (int i = 0 ; i < ((80 << RESX_SHIFT ) - 1 );
216
+ /* Render columns */
217
+ for (int i = 0 ; i < 79 ;
150
218
i ++ , vxi14 += xincX , vyi14 -= xincY , vzi14 += xincZ ) {
151
- int t = 512 ; // (256 * dz) - r2i - r1i;
219
+ int t = 512 ; /* Depth accumulation: (256 * dz) - r2i - r1i */
152
220
153
221
/* Assume t = 512, t * vxi >> 8 == vxi << 1 */
154
222
int16_t px = p0x + (vxi14 >> 5 );
@@ -158,6 +226,7 @@ int main()
158
226
int16_t ly0 = (sAcB - cA ) >> 2 ;
159
227
int16_t lz0 = (- cAcB - sA ) >> 2 ;
160
228
for (;;) {
229
+ /* Distance from torus surface */
161
230
int t0 , t1 , t2 , d ;
162
231
int16_t lx = lx0 , ly = ly0 , lz = lz0 ;
163
232
t0 = length_cordic (px , py , & lx , ly );
@@ -166,15 +235,15 @@ int main()
166
235
d = t2 - r1i ;
167
236
t += d ;
168
237
169
- if (t > 8 * 256 ) {
170
- putchar (' ' );
238
+ if (t > (8 * 256 )) {
239
+ int N = (((j - frame ) >> 3 ) ^ (((i + frame ) >> 3 ))) & 1 ;
240
+ setpixel (i , j , (N << 2 ) + 26 );
171
241
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 ++ ;
242
+ }
243
+ if (d < 2 ) {
244
+ int N = lz >> 8 ;
245
+ N = N > 0 ? N < 26 ? N : 25 : 0 ;
246
+ setpixel (i , j , N );
178
247
break ;
179
248
}
180
249
@@ -184,8 +253,9 @@ int main()
184
253
* py += d*vyi14 >> 14;
185
254
* pz += d*vzi14 >> 14;
186
255
*
187
- * idea is to make a 3d vector mul hw peripheral
188
- * equivalent to this algorithm
256
+ * Using a 3D fixed-point partial multiply approach, the
257
+ * idea is to make a 3D vector multiplication hardware
258
+ * peripheral equivalent to this algorithm.
189
259
*/
190
260
191
261
/* 11x1.14 fixed point 3x parallel multiply only 16 bit
@@ -196,14 +266,10 @@ int main()
196
266
int16_t a = vxi14 , b = vyi14 , c = vzi14 ;
197
267
while (d ) {
198
268
if (d & 1024 ) {
199
- dx += a ;
200
- dy += b ;
201
- dz += c ;
269
+ dx += a , dy += b , dz += c ;
202
270
}
203
271
d = (d & 1023 ) << 1 ;
204
- a >>= 1 ;
205
- b >>= 1 ;
206
- c >>= 1 ;
272
+ a >>= 1 , b >>= 1 , c >>= 1 ;
207
273
}
208
274
/* We have already shifted down by 10 bits, so this
209
275
* extracts the last four bits.
@@ -213,13 +279,21 @@ int main()
213
279
pz += dz >> 4 ;
214
280
}
215
281
216
- niters ++ ;
282
+ n_iters ++ ;
217
283
}
218
284
}
219
- puts ("" );
285
+ if (j & 1 )
286
+ printf ("\r\n" );
220
287
}
221
- printf ("%d iterations %d lit pixels\x1b[K" , niters , nnormals );
288
+
289
+ setcolors (25 , 33 );
290
+ printf ("%6d iterations" , n_iters );
291
+ setcolors (25 , 0 );
292
+ printf ("\x1b[K" );
293
+
294
+ #if !defined(__riscv )
222
295
fflush (stdout );
296
+ #endif
223
297
224
298
/* Rotate sines, cosines, and their products to animate the torus
225
299
* rotation about two axes.
@@ -231,10 +305,16 @@ int main()
231
305
R (6 , cAcB , cAsB );
232
306
R (6 , sAcB , sAsB );
233
307
234
- /* FIXME: Adjust tv_nsec to align with runtime expectations. */
235
- struct timespec ts = {. tv_sec = 0 , . tv_nsec = 30000 } ;
236
- nanosleep ( & ts , & ts );
308
+ #if !defined( __riscv )
309
+ usleep ( 15000 ) ;
310
+ #endif
237
311
238
- printf ("\r\x1b[%dA" , (24 << RESY_SHIFT ) - 1 );
312
+ printf ("\r\x1b[23A" );
313
+ ++ frame ;
314
+ prev_color1 = prev_color2 = -1 ;
239
315
}
316
+
317
+ /* Show cursor again */
318
+ printf ("\033[?25h" );
319
+ return 0 ;
240
320
}
0 commit comments