ltrender 0.1.3

a diff based terminal rendering engine
Documentation
use crate::{
    DrawError, SpriteRegistry,
    terminal_buffer::{BasicDrawCreator, Drawable, drawable::DoublePointed},
    update_interval_handler::UpdateIntervalCreator,
};
use ascii_assets::TerminalChar;
use common_stdx::Point;

#[derive(Clone, Debug)]
pub struct CircleDrawable {
    pub center: Point<i32>,
    pub radius: u16,
    pub border_style: TerminalChar,
    pub fill_style: Option<TerminalChar>,
}

impl DoublePointed for CircleDrawable {
    fn start(&self) -> Point<i32> {
        Point {
            x: self.center.x.saturating_sub(self.radius as i32),
            y: self.center.y.saturating_sub(self.radius as i32),
        }
    }

    fn end(&self) -> Point<i32> {
        Point {
            x: self.center.x.saturating_add(self.radius as i32),
            y: self.center.y.saturating_add(self.radius as i32),
        }
    }

    fn set_start(&mut self, p: Point<i32>) {
        let end = self.end();
        self.center = Point {
            x: (p.x + end.x) / 2,
            y: (p.y + end.y) / 2,
        };
        self.radius = ((end.x.saturating_sub(p.x)).max(end.y.saturating_sub(p.y))) as u16 / 2;
    }

    fn set_end(&mut self, p: Point<i32>) {
        let start = self.start();
        self.center = Point {
            x: (p.x + start.x) / 2,
            y: (p.y + start.y) / 2,
        };
        self.radius = ((p.x.saturating_sub(start.x)).max(p.y.saturating_sub(start.y))) as u16 / 2;
    }
}

impl Drawable for CircleDrawable {
    fn size(&self, _sprites: &SpriteRegistry) -> Result<(u16, u16), DrawError> {
        Ok((self.radius * 2, self.radius * 2))
    }
    fn as_double_pointed_mut(&mut self) -> Option<&mut dyn DoublePointed> {
        Some(self)
    }

    fn draw(&mut self, _sprites: &SpriteRegistry) -> Result<BasicDrawCreator, DrawError> {
        let mut bd_creator = BasicDrawCreator::new();

        let r2 = (self.radius as i32).pow(2);
        let r_inner2 = (self.radius.saturating_sub(1) as i32).pow(2);

        for dy in -(self.radius as i32)..=(self.radius as i32) {
            for dx in -(self.radius as i32)..=(self.radius as i32) {
                let dist2 = dx * dx + dy * dy;
                let x = self.center.x + dx;
                let y = self.center.y + dy;

                if dist2 <= r2 {
                    let chr = if dist2 >= r_inner2 {
                        self.border_style
                    } else {
                        match self.fill_style {
                            Some(ch) => ch,
                            None => continue,
                        }
                    };

                    bd_creator.draw_char(Point { x, y }, chr);
                }
            }
        }

        Ok(bd_creator)
    }

    fn bounding_iv(&self, _sprites: &SpriteRegistry) -> Option<UpdateIntervalCreator> {
        let mut c = UpdateIntervalCreator::new();

        let r = self.radius as i32;
        let center = Point {
            x: self.center.x,
            y: self.center.y,
        };

        for dy in 0..=2 * r {
            let y = center.y + dy.saturating_sub(r);
            let dist_y = (dy - r).pow(2);
            if r.pow(2) >= dist_y {
                let dx = (((r * r) - dist_y) as f64).sqrt() as i32;
                let start = center.x.saturating_sub(dx);
                let end = center.x + dx + 1;
                c.add_interval(y, (start, end));
            }
        }

        Some(c)
    }
}