quaso 0.50.0

Toolset for making Micro Games quickly
Documentation
use spitfire_draw::{
    context::DrawContext,
    utils::{Drawable, Vertex},
};
use spitfire_glow::graphics::GraphicsTarget;
use vek::{Aabr, Rect, Vec2};

#[derive(Debug, Default, Clone)]
pub struct Interactible {
    pub vertices: Vec<Vec2<f32>>,
    pub triangles: Vec<[u32; 3]>,
}

impl Interactible {
    pub fn new(
        vertices: impl IntoIterator<Item = Vec2<f32>>,
        triangles: impl IntoIterator<Item = [u32; 3]>,
    ) -> Self {
        Self {
            vertices: vertices.into_iter().collect(),
            triangles: triangles.into_iter().collect(),
        }
    }

    pub fn from_rect(rect: Rect<f32, f32>) -> Self {
        let vertices = vec![
            Vec2::new(rect.x, rect.y),
            Vec2::new(rect.x + rect.w, rect.y),
            Vec2::new(rect.x + rect.w, rect.y + rect.h),
            Vec2::new(rect.x, rect.y + rect.h),
        ];
        let triangles = vec![[0, 1, 2], [0, 2, 3]];
        Self {
            vertices,
            triangles,
        }
    }

    pub fn vertex(mut self, vertex: impl Into<Vec2<f32>>) -> Self {
        self.add_vertex(vertex);
        self
    }

    pub fn triangle(mut self, triangle: [u32; 3]) -> Self {
        self.add_triangle(triangle);
        self
    }

    pub fn add_vertex(&mut self, vertex: impl Into<Vec2<f32>>) {
        self.vertices.push(vertex.into());
    }

    pub fn add_triangle(&mut self, triangle: [u32; 3]) {
        self.triangles.push(triangle);
    }

    pub fn bounding_box(&self) -> Aabr<f32> {
        if self.vertices.is_empty() {
            return Default::default();
        }

        let mut bbox = Aabr::new_empty(self.vertices[0]);
        for vertex in &self.vertices[1..] {
            bbox = bbox.expanded_to_contain_point(*vertex);
        }
        bbox
    }

    pub fn contains_point(&self, point: impl Into<Vec2<f32>>) -> bool {
        let point = point.into();

        for triangle in &self.triangles {
            let v0 = self.vertices[triangle[0] as usize];
            let v1 = self.vertices[triangle[1] as usize];
            let v2 = self.vertices[triangle[2] as usize];

            let dx = point.x - v2.x;
            let dy = point.y - v2.y;
            let dx21 = v2.x - v1.x;
            let dy12 = v1.y - v2.y;
            let d = dy12 * (v0.x - v2.x) + dx21 * (v0.y - v2.y);
            let s = dy12 * dx + dx21 * dy;
            let t = (v2.y - v0.y) * dx + (v0.x - v2.x) * dy;

            if d < 0.0 {
                if s <= 0.0 && t <= 0.0 && s + t >= d {
                    return true;
                }
            } else if s >= 0.0 && t >= 0.0 && s + t <= d {
                return true;
            }
        }

        false
    }
}

pub struct DrawableInteractible<T: Drawable> {
    pub drawable: T,
    pub interactible: Interactible,
}

impl<T: Drawable> DrawableInteractible<T> {
    pub fn new(drawable: T) -> Self {
        Self {
            drawable,
            interactible: Interactible::default(),
        }
    }

    pub fn draw(
        mut self,
        context: &mut DrawContext,
        graphics: &mut dyn GraphicsTarget<Vertex>,
    ) -> Interactible {
        let vertex_offset = graphics.state().stream.vertices().len();
        let triangle_offset = graphics.state().stream.triangles().len();
        self.drawable.draw(context, graphics);
        self.interactible
            .vertices
            .reserve_exact(graphics.state().stream.vertices().len() - vertex_offset);
        self.interactible
            .triangles
            .reserve_exact(graphics.state().stream.triangles().len() - triangle_offset);
        for vertex in graphics.state().stream.vertices()[vertex_offset..].iter() {
            self.interactible.add_vertex(vertex.position);
        }
        for triangle in graphics.state().stream.triangles()[triangle_offset..].iter() {
            self.interactible.add_triangle([
                triangle.a - vertex_offset as u32,
                triangle.b - vertex_offset as u32,
                triangle.c - vertex_offset as u32,
            ]);
        }
        self.interactible
    }
}