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
+ #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" ,
44
53
};
45
54
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
+
46
99
/* Torus radius and camera distance.
47
100
* These values are closely tied to other constants, so modifying them
48
101
* significantly may lead to unexpected behavior.
@@ -60,6 +113,12 @@ static const int dz = 5, r1 = 1, r2 = 2;
60
113
x -= (y >> s); \
61
114
y += (x >> s)
62
115
116
+ #if PRECISE
117
+ #define N_CORDIC 10
118
+ #else
119
+ #define N_CORDIC 6
120
+ #endif
121
+
63
122
/* CORDIC algorithm used to calculate the magnitude of the vector |x, y| by
64
123
* rotating the vector onto the x-axis. This operation also transforms vector
65
124
* (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;
68
127
* noting that only one of the two lighting normal coordinates needs to be
69
128
* retained.
70
129
*/
71
- #define N_CORDIC 6
72
130
static int length_cordic (int16_t x , int16_t y , int16_t * x2_ , int16_t y2 )
73
131
{
74
132
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 ) {
76
136
x = - x ;
77
137
x2 = - x2 ;
78
138
}
139
+
140
+ /* CORDIC iterations */
79
141
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 );
93
149
}
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
97
154
*/
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
+
99
160
return (x >> 1 ) + (x >> 3 ) - (x >> 6 );
100
161
}
101
162
102
163
int main ()
103
164
{
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
+
104
174
/* Precise rotation directions, sines, cosines, and their products */
105
175
int16_t sB = 0 , cB = 16384 ;
106
176
int16_t sA = 11583 , cA = 11583 ;
107
177
int16_t sAsB = 0 , cAsB = 0 ;
108
178
int16_t sAcB = 11583 , cAcB = 11583 ;
109
179
110
180
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
112
183
* (sb + (sb << 2)) >> 6.
113
184
*/
114
185
const int16_t p0x = dz * sB >> 6 ;
115
186
const int16_t p0y = dz * sAcB >> 6 ;
116
187
const int16_t p0z = - dz * cAcB >> 6 ;
117
188
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 ;
120
192
121
- int niters = 0 ;
122
- int nnormals = 0 ;
123
193
/* per-row increments
124
194
* These can all be compiled into two shifts and an add.
125
195
*/
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 */
128
198
129
199
/* 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 */
133
203
134
204
/* 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 */
137
207
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 */
143
210
211
+ /* Render rows */
212
+ for (int j = 0 ; j < 46 ; j ++ , ycA += yincC >> 1 , ysA += yincS >> 1 ) {
144
213
/* 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 ;
148
217
149
- for (int i = 0 ; i < ((80 << RESX_SHIFT ) - 1 );
218
+ /* Render columns */
219
+ for (int i = 0 ; i < 79 ;
150
220
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 */
152
222
153
223
/* Assume t = 512, t * vxi >> 8 == vxi << 1 */
154
224
int16_t px = p0x + (vxi14 >> 5 );
@@ -158,6 +228,7 @@ int main()
158
228
int16_t ly0 = (sAcB - cA ) >> 2 ;
159
229
int16_t lz0 = (- cAcB - sA ) >> 2 ;
160
230
for (;;) {
231
+ /* Distance from torus surface */
161
232
int t0 , t1 , t2 , d ;
162
233
int16_t lx = lx0 , ly = ly0 , lz = lz0 ;
163
234
t0 = length_cordic (px , py , & lx , ly );
@@ -166,26 +237,27 @@ int main()
166
237
d = t2 - r1i ;
167
238
t += d ;
168
239
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 );
171
243
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 );
178
249
break ;
179
250
}
180
251
252
+ #ifdef USE_MULTIPLIER
253
+ px += d * vxi14 >> 14 ;
254
+ py += d * vyi14 >> 14 ;
255
+ pz += d * vzi14 >> 14 ;
256
+ #else
181
257
{
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.
189
261
*/
190
262
191
263
/* 11x1.14 fixed point 3x parallel multiply only 16 bit
@@ -196,14 +268,10 @@ int main()
196
268
int16_t a = vxi14 , b = vyi14 , c = vzi14 ;
197
269
while (d ) {
198
270
if (d & 1024 ) {
199
- dx += a ;
200
- dy += b ;
201
- dz += c ;
271
+ dx += a , dy += b , dz += c ;
202
272
}
203
273
d = (d & 1023 ) << 1 ;
204
- a >>= 1 ;
205
- b >>= 1 ;
206
- c >>= 1 ;
274
+ a >>= 1 , b >>= 1 , c >>= 1 ;
207
275
}
208
276
/* We have already shifted down by 10 bits, so this
209
277
* extracts the last four bits.
@@ -212,14 +280,23 @@ int main()
212
280
py += dy >> 4 ;
213
281
pz += dz >> 4 ;
214
282
}
283
+ #endif
215
284
216
- niters ++ ;
285
+ n_iters ++ ;
217
286
}
218
287
}
219
- puts ("" );
288
+ if (j & 1 )
289
+ printf ("\r\n" );
220
290
}
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 )
222
298
fflush (stdout );
299
+ #endif
223
300
224
301
/* Rotate sines, cosines, and their products to animate the torus
225
302
* rotation about two axes.
@@ -231,10 +308,16 @@ int main()
231
308
R (6 , cAcB , cAsB );
232
309
R (6 , sAcB , sAsB );
233
310
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
237
314
238
- printf ("\r\x1b[%dA" , (24 << RESY_SHIFT ) - 1 );
315
+ printf ("\r\x1b[23A" );
316
+ ++ frame ;
317
+ prev_color1 = prev_color2 = -1 ;
239
318
}
319
+
320
+ /* Show cursor again */
321
+ printf ("\033[?25h" );
322
+ return 0 ;
240
323
}
0 commit comments