summaryrefslogtreecommitdiffstats
path: root/_projects/matrix-digital-rain.md
diff options
context:
space:
mode:
Diffstat (limited to '_projects/matrix-digital-rain.md')
-rw-r--r--_projects/matrix-digital-rain.md98
1 files changed, 88 insertions, 10 deletions
diff --git a/_projects/matrix-digital-rain.md b/_projects/matrix-digital-rain.md
index f9a1a22..35a3d97 100644
--- a/_projects/matrix-digital-rain.md
+++ b/_projects/matrix-digital-rain.md
@@ -5,7 +5,8 @@ thumbnail: thumb.png
layout: post
---
-The famous digital rain from The Matrix, implemented in C.
+"All I see is blonde, brunette, red head." The iconic digital rain from The
+Matrix, implemented in C, without dependencies (not even ncurses).
<video style="max-width:100%;" controls="" poster="thumb.png">
<source src="matrix.mp4" type="video/mp4">
@@ -13,17 +14,94 @@ The famous digital rain from The Matrix, implemented in C.
This project is a fork of Domsson's beautiful <a
href="https://github.com/domsson/fakesteak" class="external" target="_blank"
-rel="noopener noreferrer">Fakesteak</a>.
+rel="noopener noreferrer">Fakesteak</a>. Use the following commands to compile
+and run the program:
-There are three color settings: head, tail, and background. They are configured
-by setting the 24-bit RGB channels using `COLOR_*_RED`, `COLOR_*_GRN`, and
-`COLOR_*_BLU` definitions. The ghosting effect of old monochrome screens is
-achieved by scaling the RGB channels. This results in a rain effect that
-closely resembles the original from the first Matrix movie.
+```
+$ cc -O3 main.c -o matrix
+$ ./matrix
+```
-In addition, this implementation supports UTF-32 character sets. The
-`UNICODE_MIN` and `UNICODE_MAX` controls the Unicode block used. For
-instance, setting them to `0x30A1` and `0x30F6` rains Katakana:
+While I loved Domsson's take on the
+digital rain, what blew my mind was the minimalistic elegance of his code. As I
+carefully examined it, I thought about what it might take to recreate the
+original digital rain from the first Matrix movie with it. The challenge is
+adding these features without destroying fakesteak's elegance.
+
+## How does it work?
+
+The `matrix` struct makes use of three 2D arrays to encode the Matrix: the
+`code` array for 32-bit Unicode characters, the `rgb` array for 24-bit RGB
+values of the character (foreground color), and the `shade` array for the
+degree of transparency of the character to simulate the ghosting effect of old
+monochrome displays. The dimensions of these arrays depend on the size of the
+terminal screen. Each slot in the array corresponds to a cursor position on the
+screen.
+
+The ghosting effect, which is arguably the crowning feature of my version, is
+implemented by carefully scaling and mixing the RGB channels:
+
+```
+static void mat_shade(matrix *mat, size_t row, size_t col)
+{
+ unsigned char *color;
+ color = mat->rgb[mat_idx(mat, row, col)].color;
+ color[R] = color[R] - (color[R] - COLOR_BG_RED) / 2;
+ color[G] = color[G] - (color[G] - COLOR_BG_GRN) / 2;
+ color[B] = color[B] - (color[B] - COLOR_BG_BLU) / 2;
+}
+```
+
+The above algorithm achieves transparency by iteratively bringing the
+foreground color closer to the background color with each pass of the rain.
+This approach offers multiple advantages, such as simpler and more natural
+color configuration (background, foreground, and the color of the first drop)
+that lends itself well to Unix ricing, and of course, recreates The Matrix rain
+with high fidelity.
+
+Rather than heavy-weight graphics tool kits, we use ANSI escape codes to
+control the terminal screen. It's the effective use of the ANSI escape codes
+that greatly contributes to the minimalism of the solution:
+
+```
+static void term_print(const matrix *mat, size_t row, size_t col)
+{
+ size_t idx;
+ idx = mat_idx(mat, row, col);
+ wprintf(L"\x1b[%d;%dH\x1b[38;2;%d;%d;%dm%lc",
+ row, col,
+ mat->rgb[idx].color[R],
+ mat->rgb[idx].color[G],
+ mat->rgb[idx].color[B],
+ mat->code[idx]);
+}
+```
+
+Finally, the glitch effect is controlled by the following code:
+
+```
+if (mat.row[i] > 0 && rand() % 6 == 0) {
+ j = rand() % mat.row[i];
+ if (mat.code[mat_idx(&mat, j, mat.col[i])] != ' ') {
+ mat_put_code(&mat, j, mat.col[i]);
+ term_print(&mat, j, mat.col[i]);
+ }
+}
+```
+
+The above code causes glitches in the Matrix with a probablity of 1/6.
+
+## Customizing the rain
+
+While you can customize almost any aspect of the rain including its speed,
+glitch frequency, and the density of the rain, the most useful settings for
+ricing are the color scheme and the character set used for the rain.
+
+There are three color settings: the head, the tail, and the background. They
+are configured by setting the `COLOR_*_RED`, `COLOR_*_GRN`, and `COLOR_*_BLU`
+definitions in main.c. The `UNICODE_MIN` and `UNICODE_MAX` values control the
+Unicode block used. For instance, setting them to `0x30A1` and `0x30F6` rains
+Katakana code points:
<img style="width: 100%;" src="katakana.png" />