spine2d 0.3.0

Pure Rust runtime for Spine 4.3 (unofficial)
Documentation
use crate::geometry::{SkeletonClipper, Triangulator};

fn assert_approx(actual: f32, expected: f32) {
    let diff = (actual - expected).abs();
    assert!(
        diff <= 0.001,
        "expected {expected}, got {actual} (diff {diff})"
    );
}

#[test]
fn triangulator_matches_spine_c_unit_test_rectangle() {
    let triangulator = Triangulator;

    let polygon = vec![0.0, 0.0, 100.0, 0.0, 100.0, 100.0, 0.0, 100.0];
    let triangles = triangulator.triangulate(&polygon);
    assert_eq!(triangles, vec![3, 0, 1, 3, 1, 2]);

    let polys = triangulator.decompose(&polygon, &triangles);
    assert_eq!(polys.len(), 1);
    let poly0 = &polys[0];
    assert_eq!(poly0.len(), 8);
    assert_approx(poly0[0], 0.0);
    assert_approx(poly0[1], 100.0);
    assert_approx(poly0[2], 0.0);
    assert_approx(poly0[3], 0.0);
    assert_approx(poly0[4], 100.0);
    assert_approx(poly0[5], 0.0);
    assert_approx(poly0[6], 100.0);
    assert_approx(poly0[7], 100.0);
}

#[test]
fn skeleton_clipper_clip_triangles_matches_spine_c_unit_test() {
    let mut clipper = SkeletonClipper::default();

    let clip_polygon = vec![0.0, 50.0, 100.0, 50.0, 100.0, 70.0, 0.0, 70.0];
    assert!(clipper.clip_start(&clip_polygon, false, false));
    assert!(clipper.is_clipping());

    let vertices = vec![0.0, 0.0, 100.0, 0.0, 50.0, 150.0];
    let uvs = vec![0.0, 0.0, 1.0, 0.0, 0.5, 1.0];
    let indices: Vec<u16> = vec![0, 1, 2];

    let (clipped_vertices, clipped_uvs, clipped_indices) =
        clipper.clip_triangles(&vertices, &indices, &uvs, 2);

    let expected_vertices = vec![
        76.666_664, 70.0, 23.333_334, 70.0, 16.666_672, 50.0, 83.333_33, 50.0,
    ];
    assert_eq!(clipped_vertices.len(), expected_vertices.len());
    for (actual, expected) in clipped_vertices.iter().copied().zip(expected_vertices) {
        assert_approx(actual, expected);
    }

    let expected_uvs = vec![
        0.766667, 0.466667, 0.233333, 0.466667, 0.166667, 0.333333, 0.833333, 0.333333,
    ];
    assert_eq!(clipped_uvs.len(), expected_uvs.len());
    for (actual, expected) in clipped_uvs.iter().copied().zip(expected_uvs) {
        assert_approx(actual, expected);
    }

    assert_eq!(clipped_indices, vec![0, 1, 2, 0, 2, 3]);
}

#[test]
fn skeleton_clipper_inverse_keeps_triangles_outside_clip_polygon() {
    let mut clipper = SkeletonClipper::default();

    let clip_polygon = vec![-1000.0, 50.0, 1000.0, 50.0, 1000.0, 70.0, -1000.0, 70.0];
    assert!(clipper.clip_start(&clip_polygon, false, true));

    let vertices = vec![0.0, 0.0, 100.0, 0.0, 50.0, 150.0];
    let uvs = vec![0.0, 0.0, 1.0, 0.0, 0.5, 1.0];
    let indices: Vec<u16> = vec![0, 1, 2];

    let (clipped_vertices, clipped_uvs, clipped_indices) =
        clipper.clip_triangles(&vertices, &indices, &uvs, 2);

    let expected_vertices = vec![
        76.666_664, 70.0, 50.0, 150.0, 23.333_334, 70.0, 16.666_672, 50.0, 0.0, 0.0, 100.0, 0.0,
        83.333_33, 50.0,
    ];
    assert_eq!(
        clipped_vertices.len(),
        expected_vertices.len(),
        "vertices: {clipped_vertices:?}"
    );
    for (actual, expected) in clipped_vertices.iter().copied().zip(expected_vertices) {
        assert_approx(actual, expected);
    }

    let expected_uvs = vec![
        0.766667, 0.466667, 0.5, 1.0, 0.233333, 0.466667, 0.166667, 0.333333, 0.0, 0.0, 1.0, 0.0,
        0.833333, 0.333333,
    ];
    assert_eq!(
        clipped_uvs.len(),
        expected_uvs.len(),
        "uvs: {clipped_uvs:?}"
    );
    for (actual, expected) in clipped_uvs.iter().copied().zip(expected_uvs) {
        assert_approx(actual, expected);
    }

    assert_eq!(clipped_indices, vec![0, 1, 2, 3, 4, 5, 3, 5, 6]);
}