Tuesday, September 3, 2024

donut.c converted to donut.rs (Rust)

 I tried to convert Andy Sloane's donut.c to Rust programming language. Suggestions are welcome.

//
// donut -- rotating donut
//
// note: ported a C version by CJKim - July 9, 2024
//
// the original C version's author is Andy Sloane
//

use std::{env, thread, time::Duration};
use getopts::Options;

fn donut(delay: u64) {
    const GRIDSIZE: usize = 1760;
    const TEXTURE: [char; 12] = ['.',',','-','~', ':', ';', '=', '!', '*', '#', '$', '@'];
    let mut rot_a: f32 = 0.0;
    let mut rot_b: f32 = 0.0;

    let mut iter: usize = 0;
    loop {
        iter += 1;
        let mut z: [f32;  GRIDSIZE] = [0.0; GRIDSIZE];
        let mut b: [char; GRIDSIZE] = [' '; GRIDSIZE];
        let mut grid = b.clone();
        let mut j: f32 = 0.0;
        while j < 6.28 {
            let mut i: f32 = 0.0;
            while i < 6.28 {
                let c: f32 = i.sin();
                let d: f32 = j.cos();
                let e: f32 = rot_a.sin();
                let f: f32 = j.sin();
                let g: f32 = rot_a.cos();
                let h: f32 = d + 2.0;
                let deg: f32 = 1.0 / (c * h * e + f * g + 5.0);
                let l: f32 = i.cos();
                let m: f32 = rot_b.cos();
                let n: f32 = rot_b.sin();
                let t: f32 = c * h * g - f * e;
                let x = (40.0 + 30.0 * deg * (l * h * m - t * n)) as i32;
                let y = (12.0 + 15.0 * deg * (l * h * n + t * m)) as i32;
                let o = (x + 80 * y) as usize;
                let idx: usize = (8.0 * ((f * e - c * d * g) * m
                                - c * d * e - f * g - l * d * n)) as usize;
                if 22 > y && y > 0 && x > 0 && 80 > x && deg > z[o as usize] {
                    z[o] = deg;
                    let pidx: usize = if idx > 0 { idx } else { 0 };
                    b[o] = TEXTURE[pidx];
                }
                i += 0.02;
            }
            j += 0.07;
        }
        for k in 0..GRIDSIZE {
            // putchar(k % 80 ? b[k] : 10);
            let chr = if k % 80 == 0 { '\n' } else { b[k as usize] };
            grid[k as usize] = chr;
            rot_a += 0.00004;
            rot_b += 0.00002;
        }
        let ostr = String::from_iter(grid);     // convert chars to a string
                println!("\x1b[H{}\n{}", ostr, iter);
        // BUG: when rot_a reaches 1024 and rot_b 512, the donut no longer spins
        if rot_a > 1000.0 || rot_b > 500.0 {
            rot_a = 0.0;
            rot_b = 0.0;
        }
        thread::sleep(Duration::from_millis(delay));
    }
}

fn print_usage(prgnam: &str, opts: Options) {
    let usage = format!("usage: {} [options]", prgnam);
    println!("{}", opts.usage(&usage));
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let prognam = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("d", "delay", "number of milliseconds between iterations (default=30)", "milsecs");
    opts.optflag("h", "help", "print this help message and exit");
    let matches = match opts.parse(&args[1..]) {
        Ok(m)  => { m }
        Err(e) => {
            println!("{}", e.to_string());
            print_usage(&prognam, opts);
            return;
        }
    };
    if matches.opt_present("h") {
        print_usage(&prognam, opts);
        return;
    }
    let delay: u64 = match matches.opt_str("d") {
        Some(m) => {
            match m.parse() {
                Ok(x)  => { x }
                Err(e) => {
                    println!("{} - {}", "bad milliseconds specified", e);
                    print_usage(&prognam, opts);
                    return;
                }
            }
        }
        _ => { 30 as u64 }
    };
    print!("\x1b[2J\x1b[HHello, donut lovers! [Hit ^C to stop it.]");
    donut(delay);
}



donut.c converted to donut.py (Python)

Here's my version of Andy Sloane's donut.c converted to Python programming language.

#!/usr/bin/env python3
#
# python port of donut.c
#

import sys
import time
import math
import itertools

def donut() -> list:
    z = list(itertools.repeat(0.0, 1760))
    b = list(itertools.repeat(' ', 1760))
    j = 0.0
    while j < 6.28:
        i = 0.0
        while i < 6.28:
            c: float = math.sin(i)
            d: float = math.cos(j)
            e: float = math.sin(A)
            f: float = math.sin(j);
            g: float = math.cos(A);
            h: float = d + 2.0;
            D: float = 1.0 / (c * h * e + f * g + 5.0);
            l: float = math.cos(i);
            m: float = math.cos(B);
            n: float = math.sin(B);
            t: float = c * h * g - f * e;
            x = int(40 + 30 * D * (l * h * m - t * n))
            y = int(12 + 15 * D * (l * h * n + t * m))
            o = int(x + 80 * y)
            N = int(8 * ((f * e - c * d * g) * m - c * d * e - f * g - l * d * n))
            if 22 > y and y > 0 and x > 0 and 80 > x and D > z[o]:
                z[o] = D
                b[o] = ".,-~:;=!*#$@"[N if N > 0 else 0]
            i += 0.02
        j += 0.07
    for k in range(0, 1760, 80):
        b[k] = '\n'
    return b

A: float = 0.0
B: float = 0.0
sys.stdout.write('\x1b[2J')
while True:
    sys.stdout.write('\x1b[H' + ''.join(donut()) + '\n')
    A += 0.04
    B += 0.02
    time.sleep(0.03)



Sunday, September 1, 2024

Spinning Donut

Spinning Donut