aada 0.1.0

纯 Rust 的 2D 骨骼动画引擎,兼容 Spine 4.x 导出格式(JSON + atlas)。求解到分层 Mesh 与各 Mesh 渲染设置为止,之后交给渲染器。
Documentation
//! 通用小工具:默认值便捷函数、Option 写回 trait、占位类型宏、BMP 编码。

/// `T::default()` 的便捷函数,用于减少 `..Default::default()` 噪音
pub fn default<T: Default>() -> T {
    T::default()
}
/// `Option<T>` 写回 target 的 trait(按引用借用,克隆值)
pub trait OptionWriteTo<T> {
    /// 若为 `Some`,把值克隆写入 `target`
    fn write_to(&self, target: &mut T);
}
impl<T> OptionWriteTo<T> for Option<T>
where
    T: Clone,
{
    fn write_to(&self, target: &mut T) {
        if let Some(value) = self {
            *target = value.clone();
        }
    }
}
/// `Option<T>` 写回 target 的 trait(消费自身,移动值)
pub trait OptionMoveTo<T> {
    /// 若为 `Some`,把值移动写入 `target`
    fn write_to(self, target: &mut T);
}

impl<T> OptionMoveTo<T> for Option<T> {
    fn write_to(self, target: &mut T) {
        if let Some(value) = self {
            *target = value;
        }
    }
}
#[test]
fn option() {
    let a: Option<i32> = Some(10);
    let mut b = 5;
    a.write_to(&mut b);
    assert_eq!(b, 10);

    let c: Option<String> = Some("hello".to_string());
    let mut d = "world".to_string();
    OptionWriteTo::write_to(&c, &mut d);
    assert_eq!(d, "hello".to_string());
    drop(c);
}

/// 创建占位符类型的宏:支持各种 trait,但任何实例化都会 panic。
///
/// 用法:
/// - `placeholder_type!("文档", Foo)`:生成一个 `new()` 会 panic 的空类型
/// - `placeholder_type!("文档", Foo, (fn bar(&self, a: i32)))`:额外生成自定义方法(调用即 panic)
#[macro_export]
macro_rules! placeholder_type {
    ($name:ident) => {
        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
        pub struct $name;

        impl std::default::Default for $name {
            fn default() -> Self {
                ::core::unimplemented!(concat!(
                    stringify!($name),
                    " is a placeholder type and cannot be instantiated"
                ))
            }
        }

        impl $name {
            #[allow(unused)]
            pub fn new() -> Self {
                ::core::unimplemented!(concat!(
                    stringify!($name),
                    " is a placeholder type and cannot be instantiated"
                ))
            }
        }
    };
    (
        $doc:literal,
        $name:ident,
        $( (fn $mname:ident$($t:tt)*) ),*
    ) => {
        #[doc = $doc]
        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
        pub struct $name;

        impl std::default::Default for $name {
            fn default() -> Self {
                ::core::unimplemented!(concat!(
                    stringify!($name),
                    " is a placeholder type and cannot be instantiated"
                ))
            }
        }

        impl $name {

            $(
                #[allow(unused)]
                pub fn $mname $($t)* {
                    ::core::unimplemented!(concat!(
                        stringify!($name),
                        " is a placeholder type and cannot be used"
                    ))
                }
            )*
        }
    };
    (
        $doc:literal,
        $name:ident
    )=>{
        placeholder_type!($doc, $name, (fn new()->Self));
    }

}

#[test]
fn aa() {
    placeholder_type!("随便的测试类型", _T11, (fn foo() -> i32), (fn bar(&self,a: i32)));
    #[allow(unreachable_code, unused)]
    fn _aa() {
        let t: _T11 = todo!();
        let _ = _T11::foo();
        t.bar(10);
    }
}

/// 把 RGBA 像素数据编码为 32 位 BGRA BMP 字节流(bottom-up,Windows 兼容)
///
/// `rgba` 长度必须为 width*height*4,字节序为 RGBA(R 在前,A 最后)。
/// BMP 内部按 BGRA 存储,这里完成字节序转换。返回可直接写盘。
pub fn write_bmp_rgba(width: u32, height: u32, rgba: &[u8]) -> Vec<u8> {
    let w = width as usize;
    let h = height as usize;
    assert_eq!(rgba.len(), w * h * 4, "rgba 长度必须是 width*height*4");
    let row_size = w * 4; // 32 位 BMP 每行天然 4 字节对齐
    let pixel_size = row_size * h;
    let file_size = 14 + 40 + pixel_size;
    let mut buf = Vec::with_capacity(file_size);

    // BITMAPFILEHEADER
    buf.extend_from_slice(b"BM");
    buf.extend_from_slice(&(file_size as u32).to_le_bytes());
    buf.extend_from_slice(&0u16.to_le_bytes());
    buf.extend_from_slice(&0u16.to_le_bytes());
    buf.extend_from_slice(&(14u32 + 40u32).to_le_bytes());

    // BITMAPINFOHEADER
    buf.extend_from_slice(&40u32.to_le_bytes());
    buf.extend_from_slice(&width.to_le_bytes());
    buf.extend_from_slice(&(height as i32).to_le_bytes()); // 正高度 = bottom-up
    buf.extend_from_slice(&1u16.to_le_bytes()); // planes
    buf.extend_from_slice(&32u16.to_le_bytes()); // bpp
    buf.extend_from_slice(&0u32.to_le_bytes()); // BI_RGB
    buf.extend_from_slice(&(pixel_size as u32).to_le_bytes());
    buf.extend_from_slice(&2835u32.to_le_bytes()); // 72 DPI X
    buf.extend_from_slice(&2835u32.to_le_bytes()); // 72 DPI Y
    buf.extend_from_slice(&0u32.to_le_bytes());
    buf.extend_from_slice(&0u32.to_le_bytes());

    // 像素:bottom-up(行从下往上),每像素 BGRA
    for row in (0..h).rev() {
        for col in 0..w {
            let i = (row * w + col) * 4;
            buf.push(rgba[i + 2]); // B
            buf.push(rgba[i + 1]); // G
            buf.push(rgba[i]); // R
            buf.push(rgba[i + 3]); // A
        }
    }
    buf
}

#[cfg(test)]
mod bmp_tests {
    use super::*;

    #[test]
    fn bmp_header_and_pixel_order() {
        let (w, h) = (4u32, 2u32);
        // 一行红、一行蓝,验证 bottom-up + BGRA 顺序
        let mut rgba = vec![0u8; (w * h * 4) as usize];
        for i in 0..4 {
            rgba[i * 4..i * 4 + 4].copy_from_slice(&[255, 0, 0, 255]); // 第一行(显示在下)红
        }
        for i in 4..8 {
            rgba[i * 4..i * 4 + 4].copy_from_slice(&[0, 0, 255, 128]); // 第二行(显示在上)半透明蓝
        }
        let bmp = write_bmp_rgba(w, h, &rgba);
        assert_eq!(bmp.len(), 14 + 40 + 4 * 2 * 4);
        assert_eq!(&bmp[0..2], b"BM");
        // 数据区起点
        assert_eq!(&bmp[10..14], &54u32.to_le_bytes());
        // 数据第一行 = 图像最底行(蓝)→ BGRA = [B,G,R,A] = [255,0,0,128]
        assert_eq!(&bmp[54..58], &[255, 0, 0, 128]);
        // 数据第二行 = 图像顶行(红)→ BGRA = [0,0,255,255]
        assert_eq!(&bmp[70..74], &[0, 0, 255, 255]);
    }
}