rustbatch 0.4.0

purely game dewelopment crate that offers simple but powerfull 2D rendering and some fast solutions for game world bottle necks
Documentation
extern crate image;

use self::image::{DynamicImage, GenericImageView, GenericImage};
use crate::math::rect::Rect;
use std::collections::HashMap;
use std::hash::Hash;
use crate::{Vect, Sprite};

/// Sheet is sprite sheet and nothing more. It will pack sprites to a rectangle and store all
/// sprites and created image. It is then easy to create batch that uses image and draw to it with
/// created sprites. Preferable use is loading all sprites
/// from folder and using their names as keys.
pub struct Sheet<T: Hash + Eq + Clone + Copy> {
    pub pic: DynamicImage,
    pub regions: HashMap<T, Sprite>,
}

impl<T: Hash + Eq + Clone + Copy> Sheet<T> {
    pub fn new(images: &mut [(T,DynamicImage)]) -> Self {
        let mut data = vec![];
        for i in 0..images.len() {

            images[i].1 = images[i].1.flipv();
            let img = &mut images[i];
            data.push((img.0.clone(), img.1.width(), img.1.height()));
        }

        let mut image_set = HashMap::new();

        for (key, img) in images {
            image_set.insert(key, img);
        }

        let (width, height, data) = pack(data);

        let mut regions = HashMap::new();

        for (key, rect) in data {
            regions.insert(key, rect);
        }

        let mut pic: DynamicImage = DynamicImage::new_rgba8(width, height);

        for (key, img) in image_set.iter() {
            let rect = regions.get(key).unwrap();
            let offset = (rect.min.x as u32, rect.min.y as u32);
            for (x, y, pix) in img.pixels() {
                pic.put_pixel(x + offset.0, y + offset.1, pix);
            }
        }

        Self {
            pic,
            regions: regions.into_iter().map(|(key, rect)| (key, Sprite::new(rect))).collect(),
        }
    }
}

/// pack packs rectangles with a decent efficiency, you can easily render whole sheet if you don't trust me
pub fn pack<T: Hash + Eq + Clone + Copy>(mut data: Vec<(T, u32, u32)>) -> (u32, u32, Vec<(T, Rect)>) {
    data.sort_by(|a, b| b.2.cmp(&a.2));

    let count = data.len();

    let start = {
        let mut length = 0;
        for i in data.iter() {
            length += i.1;
        }
        let side = (length as f32).log2() as u32;
        length = 0;
        let mut i = 0;
        while length < side {
            length += data[i].1;
            i += 1;
        }
        i
    };

    let mut point = start;
    let mut other_way_around = false;
    let mut lowest_ratio = i32::MAX;
    let mut best = vec![];

    let mut width = 0;
    let mut height = 0;

    'o: while point < count {


        let mut len = 0;
        for i in 0..point {
            len += data[i].1;
        }

        if other_way_around {
            for i in point..count {
                if data[i].1 > len {
                    break 'o;
                }
            }
        }


        let mut current = point;
        let mut breakpoints = vec![];
        while current < count {

            let mut total = 0;
            breakpoints.push(current);
            while current < count{
                total += data[current].1;
                if total > len {
                    break
                }
                current += 1;

            }
        }
        let mut hight = data[0].2;
        for i in breakpoints.iter() {
            hight += data[*i].2
        }

        let ratio = (len * hight) as i32;

        if ratio < lowest_ratio {
            lowest_ratio = ratio;
            best = breakpoints;
            width = len;
            height = hight;
        } else if ratio > lowest_ratio {
            if other_way_around {
                break;
            }
            other_way_around = true;
            point = start;
        }

        if point == 0 {
            break;
        }

        if other_way_around {point -= 1} else {point += 1};
    }

    let mut data: Vec<(T, Rect)> = data.into_iter().map(|(key, w, h)| (key, rect!(0, 0, w, h))).collect();

    best.insert(0, 0);
    best.push(data.len());

    let mut offset = Vect::ZERO;
    for i in 0..best.len() - 1 {
        for i in best[i]..best[i + 1] {
            data[i].1 = data[i].1.moved(offset);
            offset.x += data[i].1.width()
        }
        offset.y += data[best[i]].1.height();
        offset.x = 0.0;
    }
    (width, height, data)

}