organicomplex 0.7.0

Interactive complex-valued cellular automaton on 2D and 3D grids in search of that stuff - emergence, open-endedness, organicity etc.
use crate::base::ARGB;

use super::System;

impl System {
    /// Draw line
    /// Bresenham's, from Rosetta Code
    pub fn draw_line(&mut self, mut x1: i32, mut y1: i32, x2: i32, y2: i32, color: ARGB) -> Result<(), String> {
        let (dx, sx) = ((x2 - x1).abs(), (x2 - x1).signum());
        let (dy, sy) = ((y2 - y1).abs(), (y2 - y1).signum());

        let mut err = dx - dy;
        let mut e2: i32;

        let a = color.a as u32;
        let na = 0x100 - a;

        let color_mult_a: [u32; 3] = [(color.b as u32) * a, (color.g as u32) * a, (color.r as u32) * a];

        let (w, h) = (self.width, self.height);
        let buf = &mut self.buffer;

        loop {
            if (x1 >= 0) && (y1 >= 0) && (x1 < w) && (y1 < h) {
                let offs = ((y1 * w + x1) << 2) as usize;
                for c in 0..3 {
                    buf[offs + c] = ((color_mult_a[c] + (buf[offs + c] as u32) * na) >> 8) as u8;
                }
            }

            if (x1 == x2) && (y1 == y2) {
                break;
            }

            e2 = err << 1;
            if e2 > -dy {
                err -= dy;
                x1 += sx;
            }
            if e2 < dx {
                err += dx;
                y1 += sy;
            }
        }

        Ok(())
    }

    pub fn draw_rect(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: ARGB, fill: bool) -> Result<(), String> {
        let (x1, x2, y1, y2) = (x1.min(x2), x1.max(x2), y1.min(y2), y1.max(y2));

        if fill {
            if (x1 < self.width) && (x2 >= 0) && (y1 < self.height) && (y2 >= 0) { // intersects with screen
                let a = color.a as u32;
                let na = 0x100 - a;

                let color_mult_a: [u32; 3] = [(color.b as u32) * a, (color.g as u32) * a, (color.r as u32) * a];

                let x1 = x1.max(0) as usize;
                let y1 = y1.max(0) as usize;

                let x2 = (x2 + 1).min(self.width) as usize;
                let y2 = (y2 + 1).min(self.height) as usize;

                let mut offs_row = (y1 * (self.width as usize) + x1) << 2;
                let n_row_bytes = (self.width as usize) << 2;

                let vb = &mut self.buffer;

                for _y in y1..y2 {
                    let mut offs = offs_row;
                    for _x in x1..x2 {
                        for i in 0..3 {
                            vb[offs + i] = ((color_mult_a[i] + (vb[offs + i] as u32) * na) >> 8) as u8;
                        }
                        offs += 4;
                    }
                    offs_row += n_row_bytes;
                };
            }
        } else {
            // TODO: corner pixels are drawn twice, alpha accumulates
            let _ = self.draw_line(x1, y1, x2, y1, color);
            let _ = self.draw_line(x2, y1, x2, y2, color);
            let _ = self.draw_line(x2, y2, x1, y2, color);
            let _ = self.draw_line(x1, y2, x1, y1, color);
        }

        Ok(())
    }

    pub fn shade(&mut self, shade: u8) -> Result<(), String> {
        self.draw_rect(0, 0, self.width - 1, self.height - 1, ARGB{a: shade, r: 0, g: 0, b: 0}, true)
    }

    pub fn draw_frame(&mut self,
        x: i32, y: i32, width: i32, height: i32, // interior rectangle
        color_int: ARGB, color_ext: ARGB, // internal and external edge color
        thickness_binlog: i32) -> Result<(), String> {

        let th: i32 = 1 << thickness_binlog;
        for i in 1..=th {
            let ni = th - i;
            let color = ARGB {
                a: (((color_ext.a as i32) * i + (color_int.a as i32) * ni) >> thickness_binlog) as u8,
                r: (((color_ext.r as i32) * i + (color_int.r as i32) * ni) >> thickness_binlog) as u8,
                g: (((color_ext.g as i32) * i + (color_int.g as i32) * ni) >> thickness_binlog) as u8,
                b: (((color_ext.b as i32) * i + (color_int.b as i32) * ni) >> thickness_binlog) as u8
            };
            self.draw_rect(x-i, y-i, x+width+i-1, y+height+i-1, color, false)?;
        }

        Ok(())
    }

}