tear8 0.1.78

Tear8 is a Rust library that enables you to create your own games or fantasy consoles using Winit and Pixels.
Documentation
use std::ops::Sub;

use image::GenericImageView;

use crate::color::*;

#[derive(Clone)]
/// A normal Tear8 buffer
pub struct Buffer {
    raw_buffer: Vec<u8>,
    width: u32, height: u32,
}

impl Buffer {
    /// Create new buffer
    pub fn new(width: u32, height: u32) -> Self {
        Self {
            raw_buffer: vec![0; width as usize * height as usize * 4],
            width,
            height,
        }
    }
    /// Get buffer
    pub fn raw_buffer(&mut self) -> &mut [u8] {
        &mut self.raw_buffer
    }
    /// Get buffer width
    pub fn width(&self) -> u32 {
        self.width
    }
    /// Get buffer height
    pub fn height(&self) -> u32 {
        self.height
    }
}

/// A trait for render objects
pub trait Renderable {
    fn render(&mut self, buffer: &mut Buffer);
}  

#[derive(Clone, Copy)]
/// Pixel resource
pub struct Pixel {
    pub x: f32, pub y: f32,
    pub color: Color,
}

impl Pixel {
    pub fn new(x: f32, y: f32, color: Color) -> Self {
        Self {
            x, y,
            color,
        }
    }
}

impl Renderable for Pixel {
    fn render(&mut self, buffer: &mut Buffer) {
        // Check if is out of bounds
        if self.x < buffer.width() as f32 && 
            self.y < buffer.height() as f32 && 
            self.x >= 0. && 
            self.y >= 0. && 
            // Check alfa color 
            self.color.a >= 10
        {
            // Crea l'index per scegliere il pixel in cui colorare
            let index = (self.y as usize * buffer.width() as usize + self.x as usize) * 4;
            
            // Render on buffer
            buffer.raw_buffer()[index..index + 4].copy_from_slice(
                &[self.color.r, self.color.g, self.color.b, self.color.a]
            );
        } 
    }
}

// Calcola la distanza tra un punto e un'altro
/// Calculate distance from point to point
pub fn distance<T>(pos1: (T, T), pos2: (T, T)) -> f64 
where
    T: Sub<Output = T> + Into<f64>
{
    let dx = (pos1.0.into() - pos2.0.into()) as f64;
    let dy = (pos1.1.into() - pos2.1.into()) as f64;
    ((dx * dx + dy * dy) as f64).sqrt()
}

#[derive(Clone, Copy)]
/// Rect Resource
pub struct Rect {
    pub x: f32, pub y: f32,
    pub width: f32, pub height: f32,
    pub color: Color,
}

impl Rect {
    /// Create new rect
    pub fn new(x: f32, y: f32, width: f32, height: f32, color: Color) -> Self {
        Self {
            x, y,
            width, height,
            color,
        }
    }

    /// Create new square rect
    pub fn new_square(x: f32, y: f32, size: f32, color: Color) -> Self {
        Self {
            x, y, 
            width: size, height: size,
            color,
        }
    }

    // Restituisce i limiti del rettangolo
    /// Get the rect bounds
    pub fn get_bounds(&self) -> (f32, f32, f32, f32) {
        (self.x, self.y, self.x + self.width, self.y + self.height)
    }

    // Controlla se questo rettangolo interseca un altro rettangolo
    /// Check if this rectangle intersects another rectangle
    pub fn intersects(&self, other: &Self) -> bool {
        self.x < other.x + other.width &&
        self.x + self.width > other.x &&
        self.y < other.y + other.height &&
        self.y + self.height > other.y
    }

    /// Check if contains a point
    pub fn contains(&self, x: f32, y: f32) -> bool {
        x >= self.x && x < self.x + self.width && y >= self.y && y < self.y + self.height
    }

    /// Get rectangle area
    pub fn area(&self) -> f32 {
        self.width * self.height
    }

    /// Get rectangle perimeter
    pub fn perimeter(&self) -> f32 {
        2. * (self.width + self.height)
    }

    /// Get rectangle scale
    pub fn scale(&mut self) -> (f32, f32) {
        (self.width, self.height)
    }
}

impl Renderable for Rect {
    fn render(&mut self, buffer: &mut Buffer) {
        let rect_x = self.x as i32;
        let rect_y = self.y as i32;
        // Render rect
        for x in rect_x..rect_x + self.width as i32 {
            for y in rect_y..rect_y + self.height as i32 {
                let (x, y) = (x as f32, y as f32);
                // Create Pixel
                let mut pixel = Pixel::new(x, y, self.color);
                // Render pixel
                pixel.render(buffer);
            }
        }
    }
}

/// Line Resource
#[derive(Clone, Copy)]
pub struct Line {
    pub x1: f32, pub y1: f32,
    pub x2: f32, pub y2: f32,
    pub color: Color
}

impl Line {
    pub fn new(start: (f32, f32), end: (f32, f32), color: Color) -> Self {
        Self {
            x1: start.0, y1: start.1,
            x2: end.0, y2: end.1,
            color,
        }
    }
}

impl Renderable for Line {
    fn render(&mut self, buffer: &mut Buffer) {
        // Init vars
        let (x1, y1) = (self.x1 as i32, self.y1 as i32);
        let (x2, y2) = (self.x2 as i32, self.y2 as i32);
        // Calculate line
        let dx = if x1 > x2 { x1 - x2 } else { x2 - x1 };
        let sx = if x1 < x2 { 1 } else { -1 };
        let dy = if y1 > y2 { y1 - y2 } else { y2 - y1 };
        let sy = if y1 < y2 { 1 } else { -1 };
        // Handle Errors
        let mut err = if dx > dy { dx } else { -dy } / 2;
        let mut err2;
        
        let (mut x, mut y) = (x1, y1);
        loop {
            // Create pixel
            let mut pixel = Pixel::new(x as f32, y as f32, self.color);
            // Render pixel
            pixel.render(buffer);
            if x == x2 && y == y2 { break; }
            // Update errors
            err2 = err;
            if err2 > -dx { err -= dy; x += sx; }
            if err2 < dy { err += dx; y += sy; }
        }
    }
}

#[derive(Clone, Copy)]
/// A dotted line struct
pub struct DottedLine {
    pub x1: f32, pub y1: f32,
    pub x2: f32, pub y2: f32,
    pub color: Color,
}

impl DottedLine {
    // Create a new dotted line
    pub fn new(start: (f32, f32), end: (f32, f32), color: Color) -> Self {
        Self {
            x1: start.0, y1: start.1,
            x2: end.0, y2: end.1,
            color, 
        }
    }
}

impl Renderable for DottedLine {
    fn render(&mut self, buffer: &mut Buffer) { 
        // Init vars
        let (x1, y1) = (self.x1 as i32, self.y1 as i32);
        let (x2, y2) = (self.x2 as i32, self.y2 as i32);
        // Calculate line
        let dx = if x1 > x2 { x1 - x2 } else { x2 - x1 };
        let sx = if x1 < x2 { 1 } else { -1 };
        let dy = if y1 > y2 { y1 - y2 } else { y2 - y1 };
        let sy = if y1 < y2 { 1 } else { -1 };
        // Handle Errors
        let mut err = if dx > dy { dx } else { -dy } / 2;
        let mut err2;
        // Init vars for drawing
        let (mut x, mut y) = (x1, y1);
        let mut dashed = false; // Flag per determinare se il pixel corrente è parte della linea tratteggiata

        loop {
            // Render Pixel
            if dashed {
                let mut pixel  = Pixel::new(x as f32, y as f32, self.color);
                pixel.render(buffer); // Disegna il pixel solo se è parte della linea tratteggiata
            }
            // Pos Checks
            if x == x2 && y == y2 { break; }

            dashed = !dashed; // Inverti lo stato di 'dashed' per alternare i pixel disegnati
            // Update Errors
            err2 = err;
            if err2 > -dx {
                err -= dy; x += sx;
            }
            if err2 < dy {
                err += dx; y += sy;
            }
        }
    }
}

/// An Texture struct, it contains pixels loaded from an image
#[derive(Clone)]
pub struct Texture {
    width: u32,
    height: u32,
    pixels: Vec<Pixel>,
}

impl Texture {
    /// Load image
    pub fn new(path: &str) -> Self {
        let image = image::open(path)
            .expect(&format!("Unable to open image '{}'", path));
        let (width, height) = image.dimensions();
        let mut pixels = vec![];

        for (x, y, pixel) in image.pixels() {
            let color = Color::new_rgba(pixel[0], pixel[1], pixel[2], pixel[3]);
            let img_pixel = Pixel::new(x as f32, y as f32, color);
            pixels.push(img_pixel);
        }

        Self { width, height, pixels }
    }
    /// Load pixels from atlas
    pub fn new_from_atlas(path: &str, atlas_sprite_pos: (u32, u32), atlas_sprite_size: (u32, u32)) -> Self {
        // Carica l'immagine PNG
        let image = image::open(path)
            .expect(format!("Unable to open image '{}'", path).as_str())
            .crop(atlas_sprite_pos.0, atlas_sprite_pos.1, atlas_sprite_size.0, atlas_sprite_size.1);
        // Image Pixels
        let mut pixels = vec![];

        // Stampa i pixel del rettangolo
        for y in 0..atlas_sprite_size.1 {
            for x in 0..atlas_sprite_size.0 {
                let pixel = image.get_pixel(x, y);
                let color = Color::new_rgba(pixel[0], pixel[1], pixel[2], pixel[3]);
                let img_pixel = Pixel::new(x as f32, y as f32, color);
                pixels.push(img_pixel);
            }
        }
        
        Self {
            width: atlas_sprite_size.0, 
            height: atlas_sprite_size.1,
            pixels,
        }
    }
    /// Get sprite image width
    pub fn width(&self) -> u32 { self.width }
    /// Get sprite image height
    pub fn height(&self) -> u32 { self.height }
    /// Get sprite image pixels
    pub fn pixels(&self) -> &Vec<Pixel> { &self.pixels }
}

/// Sprite generic resource
#[derive(Clone)]
pub struct Sprite {
    pub x: f32, pub y: f32,
    pub flip_x: bool, pub flip_y: bool,
    texture_width: u32, 
    texture_height: u32, 
    pixels: Vec<Pixel>,
}

impl Sprite {
    /// Create new sprite resource with image
    pub fn new(texture: Texture, x: f32, y: f32) -> Self {
        Self {
            x, y,
            flip_x: false, flip_y: false,
            texture_width: texture.width(),
            texture_height: texture.height(),
            pixels: texture.pixels,
        }
    }
    /// Create rect from sprite image size & pos 
    pub fn generate_rect(&self) -> Rect {
        Rect::new(self.x, self.y, self.texture_width as f32, self.texture_height as f32, GREEN)
    }
}

impl Renderable for Sprite {
    fn render(&mut self, buffer: &mut Buffer) { 
        // Render pixels
        for pixel in &mut self.pixels {
            // Calculate half of image width & height
            let (image_width, image_height) = (self.texture_width as f32 / 2., self.texture_height as f32 / 2.);
            // Create offset
            let (offset_x, offset_y) = (self.x - image_width, self.y - image_height);   
            // Flip X & Y
            if self.flip_x { pixel.x = self.texture_width as f32 - 1. - pixel.x as f32; } 
            if self.flip_y { pixel.y = self.texture_height as f32 - 1. - pixel.y as f32; } 
            // Final pos
            pixel.x = pixel.x as f32 + offset_x;
            pixel.y = pixel.y as f32 + offset_y;
            // Render
            pixel.render(buffer);
        }
    }
}