wuple 0.4.0

Simple, Performant rendering on WGPU
Documentation
//! Structs for Building Shapes

pub mod draw;
mod font;
mod shape;
mod texture;

use ab_glyph::{Font, FontRef, Glyph, ScaleFont};
pub use draw::Draw;
pub use shape::Shape;

use wgpu::{util::DeviceExt, BindGroup, BindGroupLayout, Device, Queue};

use crate::{
    draw::Color,
    info::Info,
    vertex::{vertex, Vertex},
};

const PI_2: f32 = std::f32::consts::PI * 2.0;

pub struct Builder<'a> {
    device: &'a mut Device,
    queue: &'a mut Queue,
    texture_bind_group_layout: &'a BindGroupLayout,
    font: &'a FontRef<'a>,
    info: &'a Info<'a>,
}

impl<'a> Builder<'a> {
    pub(crate) fn new(
        device: &'a mut Device,
        queue: &'a mut Queue,
        texture_bind_group_layout: &'a BindGroupLayout,
        font: &'a FontRef<'a>,
        info: &'a Info,
    ) -> Self {
        Self {
            device,
            queue,
            texture_bind_group_layout,
            font,
            info,
        }
    }

    pub fn ellipse(&mut self, pos: (f32, f32), radius: (f32, f32), res: u32, draw: Draw) -> Shape {
        if res < 3 {
            panic!("Failed to draw ellipse, Resolution can't be less then 3");
        }

        let (clr, texture) = match draw {
            Draw::Color(clr) => (clr, None),
            Draw::Texture { texture, color } => (color, Some(texture)),
        };

        let clr = [clr.r, clr.g, clr.b];
        let mut vertices: Vec<Vertex> = vec![vertex([pos.0, pos.1, 0.0], clr, [0.0, 0.0])];
        let mut indices: Vec<u16> = Vec::new();

        for i in 0..res {
            let fi = (i as f32 / res as f32) * PI_2;
            let x = fi.cos() * radius.0 + pos.0;
            let y = fi.sin() * radius.1 + pos.1;
            vertices.push(vertex([x, y, 0.0], clr, [0.0, 0.0])); // TODO: fix texture coords
        }

        for i in 1..vertices.len() {
            indices.push(i as u16);
            indices.push(if i + 1 == vertices.len() {
                1
            } else {
                (i + 1) as u16
            });
            indices.push(0);
        }

        self.raw(vertices, indices, texture)
    }

    pub fn rect(&mut self, pos0: (f32, f32), pos1: (f32, f32), draw: Draw) -> Shape {
        let (clr, texture) = match draw {
            Draw::Color(clr) => (clr, None),
            Draw::Texture { texture, color } => (color, Some(texture)),
        };

        let clr = [clr.r, clr.g, clr.b];
        self.raw(
            vec![
                vertex([pos0.0, pos0.1, 0.0], clr, [0.0, 1.0]),
                vertex([pos1.0, pos0.1, 0.0], clr, [1.0, 1.0]),
                vertex([pos1.0, pos1.1, 0.0], clr, [1.0, 0.0]),
                vertex([pos0.0, pos1.1, 0.0], clr, [0.0, 0.0]),
            ],
            vec![0, 1, 2, 2, 3, 0],
            texture,
        )
    }

    /// Draws text on to a rect
    pub fn text(&mut self, pos: (f32, f32), scale: f32, text: String, clr: Color) -> Shape {
        let scaled_font = self.font.as_scaled(scale);
        let mut glyphs: Vec<Glyph> = Vec::new();
        let (total_width, total_height) = font::handle(scaled_font, (0.0, 0.0), &text, &mut glyphs);

        let mut texture_bytes: Vec<u8> = vec![0; (total_width * total_height * 4) as usize];
        let clr = [
            (clr.r * 255.0) as u8,
            (clr.g * 255.0) as u8,
            (clr.b * 255.0) as u8,
        ];
        for glyph in glyphs {
            if let Some(outlined) = scaled_font.outline_glyph(glyph) {
                let bounds = outlined.px_bounds();

                outlined.draw(|x, y, c| {
                    let x = x + bounds.min.x as u32;
                    let y = y + bounds.min.y as u32;
                    let ind = (x + y * total_width) as usize * 4;
                    texture_bytes[ind] = clr[0];
                    texture_bytes[ind + 1] = clr[1];
                    texture_bytes[ind + 2] = clr[2];
                    texture_bytes[ind + 3] = (c * 255.0) as u8;
                });
            }
        }

        let texture = self.create_texture(texture_bytes, (total_width, total_height));
        let size = self
            .info
            .from_pixel((total_width as f32, total_height as f32));

        self.rect(
            pos,
            (pos.0 + size.0, pos.1 + size.1),
            Draw::Texture {
                texture,
                color: crate::draw::color::WHITE,
            },
        )
    }

    pub fn raw(
        &mut self,
        vertices: Vec<Vertex>,
        indices: Vec<u16>,
        texture: Option<BindGroup>,
    ) -> Shape {
        let vertex_buffer = self
            .device
            .create_buffer_init(&wgpu::util::BufferInitDescriptor {
                label: Some("Vertex Buffer"),
                contents: bytemuck::cast_slice(&vertices),
                usage: wgpu::BufferUsages::VERTEX,
            });

        let index_buffer = self
            .device
            .create_buffer_init(&wgpu::util::BufferInitDescriptor {
                label: Some("Index Buffer"),
                contents: bytemuck::cast_slice(&indices),
                usage: wgpu::BufferUsages::INDEX,
            });

        Shape {
            vertex_buffer,
            index_buffer,
            index_count: indices.len() as u32,
            bindgroup: texture,
        }
    }

    pub fn create_texture(&mut self, bytes: Vec<u8>, size: (u32, u32)) -> BindGroup {
        texture::create(
            self.device,
            self.queue,
            self.texture_bind_group_layout,
            bytes,
            size,
        )
    }
}