From c05d696b5c8f88cfc2b056e800d86b15e24a5fe0 Mon Sep 17 00:00:00 2001 From: Rasmus Kaj Date: Thu, 30 Nov 2023 22:48:17 +0100 Subject: [PATCH] Hacked some more on this. --- README.md | 13 ++++ src/main.rs | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 197 insertions(+), 4 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..897afbd --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# ttylia - Julia fractals on tty + +This is intended to write the output to a DIGITAL VT220 terminal. + +``` +sudo chown $USER /dev/ttyUSB0 +stty -F /dev/ttyUSB0 raw 19200 +cargo run > /dev/ttyUSB0 +``` + +Note: 19200 bits per second gives about 2 frames per second (a frame +is 40x24 chars, a char is 8 data bits + start and stop bit, so 10 +bits). diff --git a/src/main.rs b/src/main.rs index 1d23399..c4332d0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,17 @@ use num_complex::Complex32; use std::{io::{self, Write}, thread::sleep, time::{Duration, Instant}}; -const CHARS: &[u8; 33] = b"@. .,:;-~+*i=xom\\//11II77OO77MMMM"; +const CHARS: &[u8; 13] = b"\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xA9\xA9\xA9\xA9"; -fn main() { +const CSI: u8 = b'\x9b'; // Start of control sequence, equivalent to "\x1B[" + +fn csi(code: &[u8]) -> Vec { + let mut res = vec![CSI]; // or vec![0x1B, b'['] for 7-bit compat. + res.extend(code); + res +} + +fn main() -> Result<(), io::Error> { const WIDTH: u16 = 40; const HEIGHT: u16 = 24; const SY: f32 = 3.2 / (HEIGHT as f32); @@ -17,10 +25,22 @@ fn main() { let mut rotor = Complex32 { re: 1., im: frame_time.as_secs_f32() * 0.2 }; let mut frame = 0; let mut out = io::stdout().lock(); - out.write_all(b"\x1B[2J").unwrap(); + + out.write_all(&csi(b"2J"))?; + out.write_all(&csi(b"?25l"))?; + out.write_all(b"\x1B)@\x1B~")?; // Enable custom character set + define_pixels(&mut out); + //out.write_all(b"\r\n\x1B#6 Hello \xA0\xA1\xA2\xA3\xA4\xA5\xA6 World\r\n").unwrap(); + //return; + loop { let frame_start = Instant::now(); c *= rotor; + /*eprintln!("{:.4} / {:+.4} {:+.4} / {:+.4}", + c.l1_norm(), + (c.l1_norm() - 1.), + (rotor.l1_norm() - 1.), + (c.l1_norm() - 1.) * (rotor.l1_norm() - 1.));*/ if c.norm_sqr() > 2. { rotor *= (1. / rotor.norm_sqr()).sqrt(); } @@ -61,9 +81,169 @@ fn main() { } } +fn define_pixels(out: &mut impl Write) { + let chars = [ + chardef([ + 0b1111_1110, + 0b1111_1110, + 0b1111_1110, + 0b1111_1110, + 0b1111_1110, + 0b1111_1110, + 0b1111_1110, + 0b1111_1110, + 0b1111_1110, + 0b0000_0000, + ]), + chardef([ + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + ]), + chardef([ // 1 pixel + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0001_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + ]), + chardef([ // 4 pixels + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0010_1000, + 0b0000_0000, + 0b0010_1000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + ]), + chardef([ // 5 pixels + 0b0000_0000, + 0b0000_0000, + 0b0100_0100, + 0b0000_0000, + 0b0001_0000, + 0b0000_0000, + 0b0100_0100, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + ]), + chardef([ // 6 pixels + 0b0000_0000, + 0b0100_1000, + 0b0000_0000, + 0b0000_0000, + 0b0100_1000, + 0b0000_0000, + 0b0000_0000, + 0b0100_1000, + 0b0000_0000, + 0b0000_0000, + ]), + chardef([ // 9 pixels + 0b0000_0010, + 0b0001_0000, + 0b1000_0000, + 0b0000_0010, + 0b0001_0000, + 0b1000_0000, + 0b0000_0010, + 0b0001_0000, + 0b1000_0000, + 0b0000_0000, + ]), + chardef([ // half + 0b1010_1010, + 0b0101_0100, + 0b1010_1010, + 0b0101_0100, + 0b1010_1010, + 0b0101_0100, + 0b1010_1010, + 0b0101_0100, + 0b1010_1010, + 0b0000_0000, + ]), + chardef([ + 0b1010_1010, + 0b0111_0100, + 0b1010_1010, + 0b0101_1100, + 0b1110_1010, + 0b0101_0100, + 0b1010_1010, + 0b0101_1100, + 0b1010_1010, + 0b0000_0000, + ]), + chardef([ + 0b1110_1110, + 0b0111_0110, + 0b1011_1010, + 0b0101_0100, + 0b1010_1010, + 0b0101_0100, + 0b1010_1010, + 0b0101_0100, + 0b1010_1010, + 0b0000_0000, + ]), + ]; + //eprintln!("Defining {} chars", chars.len()); + //eprintln!("Def: {chars:?}"); + out.write_all(b"\x1BP1;1;1{@").unwrap(); + if let Some((first, rest)) = chars.split_first() { + out.write_all(first).unwrap(); + for ch in rest { + out.write_all(b";").unwrap(); + out.write_all(ch).unwrap(); + } + } + out.write_all(b"\x1B\\").unwrap(); +} + +fn chardef(bits: [u8; 10]) -> [u8;17] { + let mut result = [0;17]; + for y in 0..6 { + for x in 0..8 { + if bits[y] & (0b10000000 >> x) > 0 { + result[x] |= 1 << y; + } + } + } + for y in 0..4 { + for x in 0..8 { + if bits[y+6] & (0b10000000 >> x) > 0 { + result[9 + x] |= 1 << y; + } + } + } + for c in &mut result { + *c += b'?'; + } + result[8] = b'/'; + //eprintln!("chardef2: {}", String::from_utf8_lossy(&result)); + result +} + + fn pixel(i: u8) -> u8 { CHARS[usize::from(i)] - // u32::from_be_bytes([0xff, i * 6, i + i / 2, i.saturating_mul(8)]) } fn julia(z: Complex32, c: Complex32, max_i: u8) -> u8 {