raui-tesselate-renderer 0.38.4

RAUI renderer that tesselates layout into vertex and index buffers
Documentation
use crate::Index;
use raui_core::{
    widget::{
        unit::text::{TextBoxDirection, TextBoxHorizontalAlign, TextBoxVerticalAlign},
        utils::{Color, Vec2},
        WidgetId,
    },
    Scalar,
};
use serde::{Deserialize, Serialize};
use std::ops::Range;

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct BatchExternalText {
    pub text: String,
    pub font: String,
    pub size: Scalar,
    pub color: Color,
    pub box_size: Vec2,
    pub horizontal_align: TextBoxHorizontalAlign,
    pub vertical_align: TextBoxVerticalAlign,
    pub direction: TextBoxDirection,
    pub matrix: [Scalar; 16],
}

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct BatchClipRect {
    pub box_size: Vec2,
    pub matrix: [Scalar; 16],
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Batch {
    None,
    ColoredTriangles(Range<usize>),
    ImageTriangles(String, Range<usize>),
    FontTriangles(String, Scalar, Range<usize>),
    ExternalText(WidgetId, BatchExternalText),
    ClipPush(BatchClipRect),
    ClipPop,
}

impl Default for Batch {
    fn default() -> Self {
        Self::None
    }
}

impl Batch {
    fn is_continuous(&self, other: &Self) -> bool {
        #[allow(clippy::suspicious_operation_groupings)]
        match (self, other) {
            (Self::None, Self::None) => true,
            (Self::ColoredTriangles(ra), Self::ColoredTriangles(rb)) => ra.end == rb.start,
            (Self::ImageTriangles(na, ra), Self::ImageTriangles(nb, rb)) => {
                na == nb && ra.end == rb.start
            }
            (Self::FontTriangles(na, sa, ra), Self::FontTriangles(nb, sb, rb)) => {
                na == nb && (sa - sb).abs() < 1.0e-6 && ra.end == rb.start
            }
            (Self::ClipPush(_), Self::ClipPush(_)) => true,
            _ => false,
        }
    }
}

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct TesselationVerticesSeparated {
    pub position: Vec<Vec2>,
    pub tex_coord: Vec<Vec2>,
    pub color: Vec<Color>,
}

#[derive(Debug)]
pub struct TesselationVerticesSeparatedSlice<'a> {
    pub position: &'a [Vec2],
    pub tex_coord: &'a [Vec2],
    pub color: &'a [Color],
}

#[derive(Debug)]
pub struct TesselationVerticesSeparatedSliceMut<'a> {
    pub position: &'a mut [Vec2],
    pub tex_coord: &'a mut [Vec2],
    pub color: &'a mut [Color],
}

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct TesselationVerticeInterleaved {
    pub position: Vec2,
    pub tex_coord: Vec2,
    pub color: Color,
}

impl TesselationVerticeInterleaved {
    pub fn new(position: Vec2, tex_coord: Vec2, color: Color) -> Self {
        TesselationVerticeInterleaved {
            position,
            tex_coord,
            color,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TesselationVertices {
    Separated(TesselationVerticesSeparated),
    Interleaved(Vec<TesselationVerticeInterleaved>),
}

impl TesselationVertices {
    pub fn as_separated(&self) -> Option<TesselationVerticesSeparatedSlice> {
        match &self {
            Self::Separated(TesselationVerticesSeparated {
                position,
                tex_coord,
                color,
            }) => Some(TesselationVerticesSeparatedSlice {
                position,
                tex_coord,
                color,
            }),
            _ => None,
        }
    }

    pub fn as_interleaved(&self) -> Option<&[TesselationVerticeInterleaved]> {
        match &self {
            Self::Interleaved(data) => Some(data),
            _ => None,
        }
    }
}

#[derive(Debug)]
pub enum TesselationVerticesSliceMut<'a> {
    Separated(TesselationVerticesSeparatedSliceMut<'a>),
    Interleaved(&'a mut [TesselationVerticeInterleaved]),
}

#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum TesselationVerticesFormat {
    Separated,
    Interleaved,
}

impl Default for TesselationVerticesFormat {
    fn default() -> Self {
        Self::Interleaved
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Tesselation {
    pub vertices: TesselationVertices,
    pub indices: Vec<Index>,
    pub batches: Vec<Batch>,
}

impl Tesselation {
    pub fn optimize_batches(&mut self) {
        if self.batches.is_empty() {
            return;
        }
        let mut size = 1;
        for window in self.batches.windows(2) {
            if !window[0].is_continuous(&window[1]) {
                size += 1;
            }
        }
        let batches = std::mem::replace(&mut self.batches, Vec::with_capacity(size));
        for batch in batches.into_iter() {
            if self.batches.is_empty() || !self.batches.last().unwrap().is_continuous(&batch) {
                self.batches.push(batch);
            } else {
                match (self.batches.last_mut().unwrap(), batch) {
                    (Batch::ColoredTriangles(ra), Batch::ColoredTriangles(rb))
                    | (Batch::ImageTriangles(_, ra), Batch::ImageTriangles(_, rb))
                    | (Batch::FontTriangles(_, _, ra), Batch::FontTriangles(_, _, rb)) => {
                        ra.end = rb.end
                    }
                    (a @ Batch::ClipPush(_), b @ Batch::ClipPush(_)) => {
                        *a = b;
                    }
                    _ => {}
                }
            }
        }
    }

    pub fn optimized_batches(mut self) -> Self {
        self.optimize_batches();
        self
    }
}