diff options
| author | Sadeep Madurange <sadeep@asciimx.com> | 2025-12-26 11:14:14 +0800 |
|---|---|---|
| committer | Sadeep Madurange <sadeep@asciimx.com> | 2025-12-26 11:27:35 +0800 |
| commit | 613316805da04c32b1d8088e754908ce63aed6e7 (patch) | |
| tree | 2046fd17f7df1336e9275e0f9fd3f5e2524ef8ee | |
| parent | 8147fb1110fd0037610de6701e83ccd29f4d0432 (diff) | |
| download | www-613316805da04c32b1d8088e754908ce63aed6e7.tar.gz | |
Matrix: more personal journal entry style.
| -rw-r--r-- | _log/matrix-digital-rain.md | 151 | ||||
| -rw-r--r-- | _site/feed.xml | 2 | ||||
| -rw-r--r-- | _site/index.html | 2 | ||||
| -rw-r--r-- | _site/log/index.html | 2 | ||||
| -rw-r--r-- | _site/log/matrix-digital-rain/index.html | 154 | ||||
| -rw-r--r-- | _site/posts.xml | 2 | ||||
| -rw-r--r-- | _site/projects/index.html | 4 |
7 files changed, 52 insertions, 265 deletions
diff --git a/_log/matrix-digital-rain.md b/_log/matrix-digital-rain.md index b691e84..d3f6ab4 100644 --- a/_log/matrix-digital-rain.md +++ b/_log/matrix-digital-rain.md @@ -1,146 +1,37 @@ --- -title: Recreating the Matrix rain with ANSI escape sequences +title: 'Matrix Rain: 2025 refactor' date: 2025-12-21 layout: post project: true thumbnail: thumb_sm.png --- -My 2022 implementation of the Matrix rain had too many loose ends. Unicode -support was inflexible: the character set had to be a single contiguous block -with no way to mix ASCII with something like Katakana; Phosphor decay level was -stored in a dedicated array--still don't understand why I did that when I had -already used bit-packing for the RGB channels; The algorithm was difficult to -decipher. The 2022 version worked, but that’s not the same thing as correct. +Fixed the Unicode issue finally. Can now mix ASCII + Katakana. -I began by placing the decay factor in the MSB of the 4-byte RGB value. The PD -value plays a somewhat analogous role to an alpha channel in that both -influence transparency. However, they work very differently. So, I avoided -labelling it A so as not to cause confusion: - -``` -enum { - R, /* Red */ - G, /* Green */ - B, /* Blue */ - PD /* Phosphor decay level */ -}; - -typedef union color_tag { - uint32_t value; - unsigned char color[4]; -} color; -``` - -The decision to use union over more portable bit twiddling was made three years -ago, as I recall, for readability. Seeing as all my systems are little-endian, -this is unlikely to cause any trouble. Besides, if union is never to be used, -why is it in the language anyway? - -The blend() function, which emulates the dim afterglow of Phosphor by eroding -the RGB channels towards the background, with minor refactoring, remains as -elegant as it did three years ago: - -``` -#define DECAY_MPLIER 2 - -static inline void blend(matrix *mat, - size_t row, size_t col) -{ - unsigned char *color; - - color = mat->rgb[index(mat, row, col)].color; - color[R] = color[R] - (color[R] - RGB_BG_RED) / DECAY_MPLIER; - color[G] = color[G] - (color[G] - RGB_BG_GRN) / DECAY_MPLIER; - color[B] = color[B] - (color[B] - RGB_BG_BLU) / DECAY_MPLIER; -} -``` - -While the memory inefficiency of Phosphor decay was a technical oversight I -hadn't noticed, the limitation around mixing nonadjacent Unicode blocks was a -nagging concern even three years ago. So, a fix was long overdue. - -In the new version, I introduced an array that enables a user to add as -many Unicode blocks as they want. The insert_code() function picks a block -from it at random, and then picks a character from that block at random: - -``` -#define UNICODE(min, max) (((uint64_t)max << 32) | min) - -static uint64_t glyphs[] = { - UNICODE(0x0021, 0x007E), /* ASCII */ - UNICODE(0xFF65, 0xFF9F), /* Half-width Katakana */ -}; - -static uint8_t glyphlen = (sizeof glyphs) / (sizeof glyphs[0]); - -static inline void insert_code(matrix *mat, - size_t row, size_t col) -{ - uint64_t block; - uint32_t unicode_min, unicode_max; - - block = glyphs[(rand() % glyphlen)]; - unicode_min = (uint32_t)block; - unicode_max = (uint32_t)(block >> 32); - - mat->code[index(mat, row, col)] = rand() - % (unicode_max - unicode_min) - + unicode_min; -} -``` - -The Unicode blocks are stored in 8-byte containers: the low four bytes form the -first codepoint and the high four bytes the last. Here, I chose bitwise -operations over unions because, first and foremost, the operations themselves -are trivial and idiomatic, and the UNICODE() macro simplifies the management of -charsets. - -The init_term() function is the arbiter of this zero-dependency software. It -prepares the graphical environment so that I can interact with it via ANSI -escape codes instead of unnecessary layers of abstraction: +<video style="max-width:100%;" controls="" poster="poster.png"> + <source src="matrix.mp4" type="video/mp4"> +</video> -``` -static inline int init_term(const struct winsize *ws) -{ - struct termios ta; +Moved Phosphor decay level into the 4th byte of the RGB union - should’ve done +this in 2022 instead of separate array. What was I thinking. - if (tcgetattr(STDIN_FILENO, &ta) == 0) { - ta.c_lflag &= ~ECHO; - if (tcsetattr(STDIN_FILENO, TCSANOW, &ta) == 0) { - wprintf(L"\x1b[48;2;%d;%d;%dm", - RGB_BG_RED, RGB_BG_GRN, RGB_BG_BLU); - wprintf(L"%s", ANSI_FONT_BOLD); - wprintf(L"%s", ANSI_CRSR_HIDE); - wprintf(L"%s", ANSI_CRSR_RESET); - wprintf(L"%s", ANSI_SCRN_CLEAR); - setvbuf(stdout, 0, _IOFBF, 0); - ioctl(STDOUT_FILENO, TIOCGWINSZ, ws); - return 1; - } - } - return 0; -} -``` +Keeping the RGB/PD union as it is. I'm aware of the portability issues, but I’m +on a little-endian machine, and I’m the only one reading this anyway. It’s +cleaner. -insert_code() seeds the Matrix, blend() creates the old monochrome CRT display -nostalgia, and ANSI control sequences paint the screen. The result is a digital -rain that captures the original Matrix aesthetic with high visual fidelity: +New charset array works. UNICODE(min, max) macro packs the range into uint64. +insert_code() unpacks block and picks random char. -``` -$ cc -O3 main.c -o matrix -$ ./matrix -``` +Full-width Katakana breaks columns. Stick to half-width (U+FF61-U+FF9F) range . +Compile with -DNOKANA to disable Katakana altogether. -<video style="max-width:100%;" controls="" poster="poster.png"> - <source src="matrix.mp4" type="video/mp4"> -</video> +blend() is still good, left it alone. -There was no cause to measure the program's performance characteristics -precisely; it's gentle on the CPU. On my ThinkPad T490 running OpenBSD, which -has a resolution of 1920x1080, it uses about 2-3% of the CPU, with occasional -jumps of up to about 8%; the cores remain silent, the fans don't whir, the rain -falls in quiet. +Tossed license and automake cruft. Just `cc -O3 main.c -o matrix`. Don't need +the ceremony. -Files: [source.tar.gz](source.tar.gz) +Performance regressions: none. Runs like a charm on the T490. 2% CPU. No +whirring fans. +Commit: +[03f8d87](https://git.asciimx.com/matrix-digital-rain/commit/?id=03f8d87ba7c2e46bd3f3cc4c772fb3a2ac740c92) diff --git a/_site/feed.xml b/_site/feed.xml index 80e38e0..017f730 100644 --- a/_site/feed.xml +++ b/_site/feed.xml @@ -1 +1 @@ -<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2025-12-25T22:47:54+08:00</updated><id>/feed.xml</id><title type="html">ASCIIMX | Log</title><author><name>W. D. Sadeep Madurange</name></author><entry><title type="html">Recreating the Matrix rain with ANSI escape sequences</title><link href="/log/matrix-digital-rain/" rel="alternate" type="text/html" title="Recreating the Matrix rain with ANSI escape sequences" /><published>2025-12-21T00:00:00+08:00</published><updated>2025-12-21T00:00:00+08:00</updated><id>/log/matrix-digital-rain</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[My 2022 implementation of the Matrix rain had too many loose ends. Unicode support was inflexible: the character set had to be a single contiguous block with no way to mix ASCII with something like Katakana; Phosphor decay level was stored in a dedicated array–still don’t understand why I did that when I had already used bit-packing for the RGB channels; The algorithm was difficult to decipher. The 2022 version worked, but that’s not the same thing as correct.]]></summary></entry><entry><title type="html">Suckless upgrade workflow</title><link href="/log/suckless-software/" rel="alternate" type="text/html" title="Suckless upgrade workflow" /><published>2025-11-30T00:00:00+08:00</published><updated>2025-11-30T00:00:00+08:00</updated><id>/log/suckless-software</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Workflow for managing suckless patches across upgrades:]]></summary></entry><entry><title type="html">Fingerprint door lock</title><link href="/log/fpm-door-lock/" rel="alternate" type="text/html" title="Fingerprint door lock" /><published>2025-08-18T00:00:00+08:00</published><updated>2025-08-18T00:00:00+08:00</updated><id>/log/fpm-door-lock</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[This project features a fingerprint door lock powered by an ATmega328P microcontroller.]]></summary></entry><entry><title type="html">On the use of MOSFETs as electronic switches</title><link href="/log/mosfet-switches/" rel="alternate" type="text/html" title="On the use of MOSFETs as electronic switches" /><published>2025-06-22T00:00:00+08:00</published><updated>2025-06-22T00:00:00+08:00</updated><id>/log/mosfet-switches</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Recently, I needed a low-power circuit for one of my battery-operated projects. Much of the system’s power savings depended on its ability to electronically switch off components, such as servos, that draw high levels of quiescent currents. My search for a solution led me to MOSFETs, transistors capable of controlling circuits operating at voltages far above their own.]]></summary></entry><entry><title type="html">How to configure ATmega328P microcontrollers to run at 3.3V and 5V</title><link href="/log/arduino-uno/" rel="alternate" type="text/html" title="How to configure ATmega328P microcontrollers to run at 3.3V and 5V" /><published>2025-06-10T00:00:00+08:00</published><updated>2025-06-10T00:00:00+08:00</updated><id>/log/arduino-uno</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[This is a quick reference for wiring up ATmega328P ICs to run at 5V and 3.3V. While the 5V configuration is common, the 3.3V configuration can be useful in low-power applications and when interfacing with parts that themselves run at 3.3V. In this guide, the 5V setup is configured with a 16MHz crystal oscillator, while the 3.3V configuration makes use of an 8MHz crystal oscillator.]]></summary></entry><entry><title type="html">My first PCB</title><link href="/log/my-first-pcb/" rel="alternate" type="text/html" title="My first PCB" /><published>2025-04-26T00:00:00+08:00</published><updated>2025-04-26T00:00:00+08:00</updated><id>/log/my-first-pcb</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[In 2023, I started tinkering with DIY electronics as a hobby. Until now, I’ve been using development boards like the Arduino Uno and ESP-32-WROOM so that I can focus on the software. Recently, I decided to step outside of my comfort zone and design a PCB from scratch for a door lock I’m working on.]]></summary></entry><entry><title type="html">Bumblebee: browser automation</title><link href="/log/bumblebee/" rel="alternate" type="text/html" title="Bumblebee: browser automation" /><published>2025-04-02T00:00:00+08:00</published><updated>2025-04-02T00:00:00+08:00</updated><id>/log/bumblebee</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Bumblebee is a tool I built for one of my employers to automate the generation of web scraping scripts.]]></summary></entry><entry><title type="html">How to set up ATSAM3X8E microcontrollers for bare-metal programming in C</title><link href="/log/arduino-due/" rel="alternate" type="text/html" title="How to set up ATSAM3X8E microcontrollers for bare-metal programming in C" /><published>2024-09-16T00:00:00+08:00</published><updated>2024-09-16T00:00:00+08:00</updated><id>/log/arduino-due</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[This article is a step-by-step guide for programming bare-metal ATSAM3X8E chips found on Arduino Due boards. It also includes notes on the chip’s memory layout relevant for writing linker scripts. The steps described in this article were tested on an OpenBSD workstation.]]></summary></entry><entry><title type="html">Etlas: e-paper dashboard</title><link href="/log/etlas/" rel="alternate" type="text/html" title="Etlas: e-paper dashboard" /><published>2024-09-05T00:00:00+08:00</published><updated>2024-09-05T00:00:00+08:00</updated><id>/log/etlas</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Etlas is a news, stock market, and weather tracker powered by an ESP32 NodeMCU D1, featuring a 7.5-inch Waveshare e-paper display and a DHT22 sensor module.]]></summary></entry><entry><title type="html">Experimental e-reader</title><link href="/log/e-reader/" rel="alternate" type="text/html" title="Experimental e-reader" /><published>2023-10-24T00:00:00+08:00</published><updated>2023-10-24T00:00:00+08:00</updated><id>/log/e-reader</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[This project features an experimental e-reader powered by an ESP-WROOM-32 development board and a 7.5-inch Waveshare e-paper display built with the intention of learning about e-paper displays.]]></summary></entry></feed>
\ No newline at end of file +<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2025-12-26T11:27:12+08:00</updated><id>/feed.xml</id><title type="html">ASCIIMX | Log</title><author><name>W. D. Sadeep Madurange</name></author><entry><title type="html">Matrix Rain: 2025 refactor</title><link href="/log/matrix-digital-rain/" rel="alternate" type="text/html" title="Matrix Rain: 2025 refactor" /><published>2025-12-21T00:00:00+08:00</published><updated>2025-12-21T00:00:00+08:00</updated><id>/log/matrix-digital-rain</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Fixed the Unicode issue finally. Can now mix ASCII + Katakana.]]></summary></entry><entry><title type="html">Suckless upgrade workflow</title><link href="/log/suckless-software/" rel="alternate" type="text/html" title="Suckless upgrade workflow" /><published>2025-11-30T00:00:00+08:00</published><updated>2025-11-30T00:00:00+08:00</updated><id>/log/suckless-software</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Workflow for managing suckless patches across upgrades:]]></summary></entry><entry><title type="html">Fingerprint door lock</title><link href="/log/fpm-door-lock/" rel="alternate" type="text/html" title="Fingerprint door lock" /><published>2025-08-18T00:00:00+08:00</published><updated>2025-08-18T00:00:00+08:00</updated><id>/log/fpm-door-lock</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[This project features a fingerprint door lock powered by an ATmega328P microcontroller.]]></summary></entry><entry><title type="html">On the use of MOSFETs as electronic switches</title><link href="/log/mosfet-switches/" rel="alternate" type="text/html" title="On the use of MOSFETs as electronic switches" /><published>2025-06-22T00:00:00+08:00</published><updated>2025-06-22T00:00:00+08:00</updated><id>/log/mosfet-switches</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Recently, I needed a low-power circuit for one of my battery-operated projects. Much of the system’s power savings depended on its ability to electronically switch off components, such as servos, that draw high levels of quiescent currents. My search for a solution led me to MOSFETs, transistors capable of controlling circuits operating at voltages far above their own.]]></summary></entry><entry><title type="html">How to configure ATmega328P microcontrollers to run at 3.3V and 5V</title><link href="/log/arduino-uno/" rel="alternate" type="text/html" title="How to configure ATmega328P microcontrollers to run at 3.3V and 5V" /><published>2025-06-10T00:00:00+08:00</published><updated>2025-06-10T00:00:00+08:00</updated><id>/log/arduino-uno</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[This is a quick reference for wiring up ATmega328P ICs to run at 5V and 3.3V. While the 5V configuration is common, the 3.3V configuration can be useful in low-power applications and when interfacing with parts that themselves run at 3.3V. In this guide, the 5V setup is configured with a 16MHz crystal oscillator, while the 3.3V configuration makes use of an 8MHz crystal oscillator.]]></summary></entry><entry><title type="html">My first PCB</title><link href="/log/my-first-pcb/" rel="alternate" type="text/html" title="My first PCB" /><published>2025-04-26T00:00:00+08:00</published><updated>2025-04-26T00:00:00+08:00</updated><id>/log/my-first-pcb</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[In 2023, I started tinkering with DIY electronics as a hobby. Until now, I’ve been using development boards like the Arduino Uno and ESP-32-WROOM so that I can focus on the software. Recently, I decided to step outside of my comfort zone and design a PCB from scratch for a door lock I’m working on.]]></summary></entry><entry><title type="html">Bumblebee: browser automation</title><link href="/log/bumblebee/" rel="alternate" type="text/html" title="Bumblebee: browser automation" /><published>2025-04-02T00:00:00+08:00</published><updated>2025-04-02T00:00:00+08:00</updated><id>/log/bumblebee</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Bumblebee is a tool I built for one of my employers to automate the generation of web scraping scripts.]]></summary></entry><entry><title type="html">How to set up ATSAM3X8E microcontrollers for bare-metal programming in C</title><link href="/log/arduino-due/" rel="alternate" type="text/html" title="How to set up ATSAM3X8E microcontrollers for bare-metal programming in C" /><published>2024-09-16T00:00:00+08:00</published><updated>2024-09-16T00:00:00+08:00</updated><id>/log/arduino-due</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[This article is a step-by-step guide for programming bare-metal ATSAM3X8E chips found on Arduino Due boards. It also includes notes on the chip’s memory layout relevant for writing linker scripts. The steps described in this article were tested on an OpenBSD workstation.]]></summary></entry><entry><title type="html">Etlas: e-paper dashboard</title><link href="/log/etlas/" rel="alternate" type="text/html" title="Etlas: e-paper dashboard" /><published>2024-09-05T00:00:00+08:00</published><updated>2024-09-05T00:00:00+08:00</updated><id>/log/etlas</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Etlas is a news, stock market, and weather tracker powered by an ESP32 NodeMCU D1, featuring a 7.5-inch Waveshare e-paper display and a DHT22 sensor module.]]></summary></entry><entry><title type="html">Experimental e-reader</title><link href="/log/e-reader/" rel="alternate" type="text/html" title="Experimental e-reader" /><published>2023-10-24T00:00:00+08:00</published><updated>2023-10-24T00:00:00+08:00</updated><id>/log/e-reader</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[This project features an experimental e-reader powered by an ESP-WROOM-32 development board and a 7.5-inch Waveshare e-paper display built with the intention of learning about e-paper displays.]]></summary></entry></feed>
\ No newline at end of file diff --git a/_site/index.html b/_site/index.html index 3bf1b02..c4aea58 100644 --- a/_site/index.html +++ b/_site/index.html @@ -56,7 +56,7 @@ <tr> <td class="posts-td posts-td-link"> - <a href="/log/matrix-digital-rain/" class="link-decor-none">Recreating the Matrix rain with ANSI escape sequences</a> + <a href="/log/matrix-digital-rain/" class="link-decor-none">Matrix Rain: 2025 refactor</a> </td> <td class="posts-td posts-td-time"> <span class="post-meta"> diff --git a/_site/log/index.html b/_site/log/index.html index fad9164..c3c652a 100644 --- a/_site/log/index.html +++ b/_site/log/index.html @@ -46,7 +46,7 @@ <tr> <td class="posts-td posts-td-link"> - <a href="/log/matrix-digital-rain/" class="link-decor-none">Recreating the Matrix rain with ANSI escape sequences</a> + <a href="/log/matrix-digital-rain/" class="link-decor-none">Matrix Rain: 2025 refactor</a> </td> <td class="posts-td posts-td-time"> <span class="post-meta"> diff --git a/_site/log/matrix-digital-rain/index.html b/_site/log/matrix-digital-rain/index.html index 7c8c659..3d393cd 100644 --- a/_site/log/matrix-digital-rain/index.html +++ b/_site/log/matrix-digital-rain/index.html @@ -2,12 +2,12 @@ <html> <head> <meta charset="utf-8"> - <title>Recreating the Matrix rain with ANSI escape sequences</title> + <title>Matrix Rain: 2025 refactor</title> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> - <title>Recreating the Matrix rain with ANSI escape sequences</title> + <title>Matrix Rain: 2025 refactor</title> <link rel="stylesheet" href="/assets/css/main.css"> <link rel="stylesheet" href="/assets/css/skeleton.css"> </head> @@ -41,142 +41,38 @@ <main> <div class="container"> <div class="container-2"> - <h2 class="center" id="title">RECREATING THE MATRIX RAIN WITH ANSI ESCAPE SEQUENCES</h2> + <h2 class="center" id="title">MATRIX RAIN: 2025 REFACTOR</h2> <h6 class="center">21 DECEMBER 2025</h5> <br> - <div class="twocol justify"><p>My 2022 implementation of the Matrix rain had too many loose ends. Unicode -support was inflexible: the character set had to be a single contiguous block -with no way to mix ASCII with something like Katakana; Phosphor decay level was -stored in a dedicated array–still don’t understand why I did that when I had -already used bit-packing for the RGB channels; The algorithm was difficult to -decipher. The 2022 version worked, but that’s not the same thing as correct.</p> - -<p>I began by placing the decay factor in the MSB of the 4-byte RGB value. The PD -value plays a somewhat analogous role to an alpha channel in that both -influence transparency. However, they work very differently. So, I avoided -labelling it A so as not to cause confusion:</p> - -<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>enum { - R, /* Red */ - G, /* Green */ - B, /* Blue */ - PD /* Phosphor decay level */ -}; - -typedef union color_tag { - uint32_t value; - unsigned char color[4]; -} color; -</code></pre></div></div> - -<p>The decision to use union over more portable bit twiddling was made three years -ago, as I recall, for readability. Seeing as all my systems are little-endian, -this is unlikely to cause any trouble. Besides, if union is never to be used, -why is it in the language anyway?</p> - -<p>The blend() function, which emulates the dim afterglow of Phosphor by eroding -the RGB channels towards the background, with minor refactoring, remains as -elegant as it did three years ago:</p> - -<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#define DECAY_MPLIER 2 - -static inline void blend(matrix *mat, - size_t row, size_t col) -{ - unsigned char *color; - - color = mat->rgb[index(mat, row, col)].color; - color[R] = color[R] - (color[R] - RGB_BG_RED) / DECAY_MPLIER; - color[G] = color[G] - (color[G] - RGB_BG_GRN) / DECAY_MPLIER; - color[B] = color[B] - (color[B] - RGB_BG_BLU) / DECAY_MPLIER; -} -</code></pre></div></div> - -<p>While the memory inefficiency of Phosphor decay was a technical oversight I -hadn’t noticed, the limitation around mixing nonadjacent Unicode blocks was a -nagging concern even three years ago. So, a fix was long overdue.</p> - -<p>In the new version, I introduced an array that enables a user to add as -many Unicode blocks as they want. The insert_code() function picks a block -from it at random, and then picks a character from that block at random:</p> - -<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#define UNICODE(min, max) (((uint64_t)max << 32) | min) - -static uint64_t glyphs[] = { - UNICODE(0x0021, 0x007E), /* ASCII */ - UNICODE(0xFF65, 0xFF9F), /* Half-width Katakana */ -}; - -static uint8_t glyphlen = (sizeof glyphs) / (sizeof glyphs[0]); - -static inline void insert_code(matrix *mat, - size_t row, size_t col) -{ - uint64_t block; - uint32_t unicode_min, unicode_max; - - block = glyphs[(rand() % glyphlen)]; - unicode_min = (uint32_t)block; - unicode_max = (uint32_t)(block >> 32); - - mat->code[index(mat, row, col)] = rand() - % (unicode_max - unicode_min) - + unicode_min; -} -</code></pre></div></div> - -<p>The Unicode blocks are stored in 8-byte containers: the low four bytes form the -first codepoint and the high four bytes the last. Here, I chose bitwise -operations over unions because, first and foremost, the operations themselves -are trivial and idiomatic, and the UNICODE() macro simplifies the management of -charsets.</p> - -<p>The init_term() function is the arbiter of this zero-dependency software. It -prepares the graphical environment so that I can interact with it via ANSI -escape codes instead of unnecessary layers of abstraction:</p> - -<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>static inline int init_term(const struct winsize *ws) -{ - struct termios ta; - - if (tcgetattr(STDIN_FILENO, &ta) == 0) { - ta.c_lflag &= ~ECHO; - if (tcsetattr(STDIN_FILENO, TCSANOW, &ta) == 0) { - wprintf(L"\x1b[48;2;%d;%d;%dm", - RGB_BG_RED, RGB_BG_GRN, RGB_BG_BLU); - wprintf(L"%s", ANSI_FONT_BOLD); - wprintf(L"%s", ANSI_CRSR_HIDE); - wprintf(L"%s", ANSI_CRSR_RESET); - wprintf(L"%s", ANSI_SCRN_CLEAR); - setvbuf(stdout, 0, _IOFBF, 0); - ioctl(STDOUT_FILENO, TIOCGWINSZ, ws); - return 1; - } - } - return 0; -} -</code></pre></div></div> - -<p>insert_code() seeds the Matrix, blend() creates the old monochrome CRT display -nostalgia, and ANSI control sequences paint the screen. The result is a digital -rain that captures the original Matrix aesthetic with high visual fidelity:</p> - -<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cc -O3 main.c -o matrix -$ ./matrix -</code></pre></div></div> + <div class="twocol justify"><p>Fixed the Unicode issue finally. Can now mix ASCII + Katakana.</p> <video style="max-width:100%;" controls="" poster="poster.png"> <source src="matrix.mp4" type="video/mp4" /> </video> -<p>There was no cause to measure the program’s performance characteristics -precisely; it’s gentle on the CPU. On my ThinkPad T490 running OpenBSD, which -has a resolution of 1920x1080, it uses about 2-3% of the CPU, with occasional -jumps of up to about 8%; the cores remain silent, the fans don’t whir, the rain -falls in quiet.</p> +<p>Moved Phosphor decay level into the 4th byte of the RGB union - should’ve done +this in 2022 instead of separate array. What was I thinking.</p> -<p>Files: <a href="source.tar.gz">source.tar.gz</a></p> +<p>Keeping the RGB/PD union as it is. I’m aware of the portability issues, but I’m +on a little-endian machine, and I’m the only one reading this anyway. It’s +cleaner.</p> +<p>New charset array works. UNICODE(min, max) macro packs the range into uint64. +insert_code() unpacks block and picks random char.</p> + +<p>Full-width Katakana breaks columns. Stick to half-width (U+FF61-U+FF9F) range . +Compile with -DNOKANA to disable Katakana altogether.</p> + +<p>blend() is still good, left it alone.</p> + +<p>Tossed license and automake cruft. Just <code class="language-plaintext highlighter-rouge">cc -O3 main.c -o matrix</code>. Don’t need +the ceremony.</p> + +<p>Performance regressions: none. Runs like a charm on the T490. 2% CPU. No +whirring fans.</p> + +<p>Commit: +<a href="https://git.asciimx.com/matrix-digital-rain/commit/?id=03f8d87ba7c2e46bd3f3cc4c772fb3a2ac740c92">03f8d87</a></p> </div> <p class="post-author right">by W. D. Sadeep Madurange</p> </div> diff --git a/_site/posts.xml b/_site/posts.xml index 1f5e654..25b0ea1 100644 --- a/_site/posts.xml +++ b/_site/posts.xml @@ -1 +1 @@ -<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="/posts.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2025-12-25T22:47:54+08:00</updated><id>/posts.xml</id><title type="html">ASCIIMX</title><author><name>W. D. Sadeep Madurange</name></author></feed>
\ No newline at end of file +<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="/posts.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2025-12-26T11:27:12+08:00</updated><id>/posts.xml</id><title type="html">ASCIIMX</title><author><name>W. D. Sadeep Madurange</name></author></feed>
\ No newline at end of file diff --git a/_site/projects/index.html b/_site/projects/index.html index c75a620..4fae8d8 100644 --- a/_site/projects/index.html +++ b/_site/projects/index.html @@ -51,8 +51,8 @@ <td class="project-item"> <a href="../log/matrix-digital-rain" class="link-decor-none"> - <img src="../log/matrix-digital-rain/thumb_sm.png" alt="Recreating the Matrix rain with ANSI escape sequences"> - <h5>Recreating the Matrix rain with ANSI escape sequences</h5> + <img src="../log/matrix-digital-rain/thumb_sm.png" alt="Matrix Rain: 2025 refactor"> + <h5>Matrix Rain: 2025 refactor</h5> </a> </td> |
