use crate::geom::{self, Point2};
use crate::glam::{vec3, EulerRot, Mat4, Quat, Vec2, Vec3};
use crate::math::{deg_to_rad, turns_to_rad};
use crate::wgpu;
use lyon::path::PathEvent;
use std::cell::RefCell;
use std::collections::HashMap;
use std::mem;
use std::rc::Rc;
pub use self::background::Background;
pub use self::drawing::{Drawing, DrawingContext};
use self::mesh::vertex::{Color, TexCoords};
pub use self::mesh::Mesh;
use self::primitive::Primitive;
pub use self::renderer::{Builder as RendererBuilder, Renderer};
pub use self::theme::Theme;
pub mod background;
mod drawing;
pub mod mesh;
pub mod primitive;
pub mod properties;
pub mod renderer;
pub mod theme;
#[derive(Clone, Debug)]
pub struct Draw {
state: Rc<RefCell<State>>,
context: Context,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Context {
pub transform: Mat4,
pub blend: wgpu::BlendState,
pub scissor: Scissor,
pub topology: wgpu::PrimitiveTopology,
pub sampler: wgpu::SamplerDescriptor<'static>,
}
#[derive(Clone, Debug)]
pub enum DrawCommand {
Primitive(Primitive),
Context(Context),
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Scissor {
Full,
Rect(geom::Rect),
NoOverlap,
}
#[derive(Clone, Debug)]
pub struct State {
last_draw_context: Option<Context>,
background_color: Option<properties::LinSrgba>,
drawing: HashMap<usize, Primitive>,
draw_commands: Vec<Option<DrawCommand>>,
intermediary_state: RefCell<IntermediaryState>,
theme: Theme,
}
#[derive(Clone, Debug)]
pub struct IntermediaryState {
intermediary_mesh: Mesh,
path_event_buffer: Vec<PathEvent>,
path_points_colored_buffer: Vec<(Point2, Color)>,
path_points_textured_buffer: Vec<(Point2, TexCoords)>,
text_buffer: String,
}
impl IntermediaryState {
pub fn reset(&mut self) {
self.intermediary_mesh.clear();
self.path_event_buffer.clear();
self.path_points_colored_buffer.clear();
self.path_points_textured_buffer.clear();
self.text_buffer.clear();
}
}
impl State {
fn reset(&mut self) {
self.background_color = None;
self.last_draw_context = None;
self.drawing.clear();
self.draw_commands.clear();
self.intermediary_state.borrow_mut().reset();
}
fn finish_remaining_drawings(&mut self) {
let mut drawing = mem::replace(&mut self.drawing, Default::default());
for (index, primitive) in drawing.drain() {
self.insert_draw_command(index, primitive);
}
mem::swap(&mut self.drawing, &mut drawing);
}
pub(crate) fn finish_drawing(&mut self, index: usize) {
if let Some(primitive) = self.drawing.remove(&index) {
self.insert_draw_command(index, primitive);
}
}
fn insert_draw_command(&mut self, index: usize, prim: Primitive) {
if let Some(elem) = self.draw_commands.get_mut(index) {
*elem = Some(DrawCommand::Primitive(prim));
}
}
}
impl Draw {
pub fn new() -> Self {
Self::default()
}
pub fn reset(&self) {
self.state.borrow_mut().reset();
}
pub fn transform(&self, transform_matrix: Mat4) -> Self {
let mut context = self.context.clone();
context.transform = context.transform * transform_matrix;
self.context(context)
}
pub fn translate(&self, v: Vec3) -> Self {
self.transform(Mat4::from_translation(v))
}
pub fn xyz(&self, v: Vec3) -> Self {
self.translate(v)
}
pub fn xy(&self, v: Vec2) -> Self {
self.xyz(v.extend(0.0))
}
pub fn x_y_z(&self, x: f32, y: f32, z: f32) -> Self {
self.xyz([x, y, z].into())
}
pub fn x_y(&self, x: f32, y: f32) -> Self {
self.xy([x, y].into())
}
pub fn x(&self, x: f32) -> Self {
self.x_y(x, 0.0)
}
pub fn y(&self, y: f32) -> Self {
self.x_y(0.0, y)
}
pub fn z(&self, z: f32) -> Self {
self.x_y_z(0.0, 0.0, z)
}
pub fn scale(&self, s: f32) -> Self {
self.scale_axes(vec3(s, s, s))
}
pub fn scale_axes(&self, v: Vec3) -> Self {
self.transform(Mat4::from_scale(v))
}
pub fn scale_x(&self, s: f32) -> Self {
self.scale_axes(vec3(s, 1.0, 1.0))
}
pub fn scale_y(&self, s: f32) -> Self {
self.scale_axes(vec3(1.0, s, 1.0))
}
pub fn scale_z(&self, s: f32) -> Self {
self.scale_axes(vec3(1.0, 1.0, s))
}
pub fn euler(&self, euler: Vec3) -> Self {
self.transform(Mat4::from_euler(EulerRot::XYZ, euler.x, euler.y, euler.z))
}
pub fn quaternion(&self, q: Quat) -> Self {
self.transform(Mat4::from_quat(q))
}
pub fn radians(&self, v: Vec3) -> Self {
self.euler(v)
}
pub fn x_radians(&self, x: f32) -> Self {
self.radians(vec3(x, 0.0, 0.0))
}
pub fn y_radians(&self, y: f32) -> Self {
self.radians(vec3(0.0, y, 0.0))
}
pub fn z_radians(&self, z: f32) -> Self {
self.radians(vec3(0.0, 0.0, z))
}
pub fn degrees(&self, v: Vec3) -> Self {
self.radians(vec3(deg_to_rad(v.x), deg_to_rad(v.y), deg_to_rad(v.z)))
}
pub fn x_degrees(&self, x: f32) -> Self {
self.x_radians(deg_to_rad(x))
}
pub fn y_degrees(&self, y: f32) -> Self {
self.y_radians(deg_to_rad(y))
}
pub fn z_degrees(&self, z: f32) -> Self {
self.z_radians(deg_to_rad(z))
}
pub fn turns(&self, v: Vec3) -> Self {
self.radians(vec3(
turns_to_rad(v.x),
turns_to_rad(v.y),
turns_to_rad(v.z),
))
}
pub fn x_turns(&self, x: f32) -> Self {
self.x_radians(turns_to_rad(x))
}
pub fn y_turns(&self, y: f32) -> Self {
self.y_radians(turns_to_rad(y))
}
pub fn z_turns(&self, z: f32) -> Self {
self.z_radians(turns_to_rad(z))
}
pub fn pitch(&self, pitch: f32) -> Self {
self.x_radians(pitch)
}
pub fn yaw(&self, yaw: f32) -> Self {
self.y_radians(yaw)
}
pub fn roll(&self, roll: f32) -> Self {
self.z_radians(roll)
}
pub fn rotate(&self, radians: f32) -> Self {
self.z_radians(radians)
}
pub fn alpha_blend(&self, blend_descriptor: wgpu::BlendComponent) -> Self {
let mut context = self.context.clone();
context.blend.alpha = blend_descriptor;
self.context(context)
}
pub fn color_blend(&self, blend_descriptor: wgpu::BlendComponent) -> Self {
let mut context = self.context.clone();
context.blend.color = blend_descriptor;
self.context(context)
}
pub fn blend(&self, blend_descriptor: wgpu::BlendComponent) -> Self {
self.color_blend(blend_descriptor)
}
pub fn scissor(&self, scissor: geom::Rect<f32>) -> Self {
let mut context = self.context.clone();
context.scissor = match context.scissor {
Scissor::Full => Scissor::Rect(scissor),
Scissor::Rect(rect) => rect
.overlap(scissor)
.map(Scissor::Rect)
.unwrap_or(Scissor::NoOverlap),
Scissor::NoOverlap => Scissor::NoOverlap,
};
self.context(context)
}
pub fn line_mode(&self) -> Self {
self.primitive_topology(wgpu::PrimitiveTopology::LineList)
}
pub fn point_mode(&self) -> Self {
self.primitive_topology(wgpu::PrimitiveTopology::PointList)
}
pub fn triangle_mode(&self) -> Self {
self.primitive_topology(wgpu::PrimitiveTopology::TriangleList)
}
pub fn sampler(&self, desc: wgpu::SamplerDescriptor<'static>) -> Self {
let mut context = self.context.clone();
context.sampler = desc;
self.context(context)
}
fn primitive_topology(&self, topology: wgpu::PrimitiveTopology) -> Self {
let mut context = self.context.clone();
context.topology = topology;
self.context(context)
}
fn context(&self, context: Context) -> Self {
let state = self.state.clone();
Draw { state, context }
}
pub fn background(&self) -> Background {
background::new(self)
}
pub fn a<T>(&self, primitive: T) -> Drawing<T>
where
T: Into<Primitive>,
Primitive: Into<Option<T>>,
{
let index = {
let mut state = self.state.borrow_mut();
if state.last_draw_context.as_ref() != Some(&self.context) {
state
.draw_commands
.push(Some(DrawCommand::Context(self.context.clone())));
state.last_draw_context = Some(self.context.clone());
}
let index = state.draw_commands.len();
let primitive: Primitive = primitive.into();
state.draw_commands.push(None);
state.drawing.insert(index, primitive);
index
};
drawing::new(self, index)
}
pub fn path(&self) -> Drawing<primitive::PathInit> {
self.a(Default::default())
}
pub fn ellipse(&self) -> Drawing<primitive::Ellipse> {
self.a(Default::default())
}
pub fn line(&self) -> Drawing<primitive::Line> {
self.a(Default::default())
}
pub fn arrow(&self) -> Drawing<primitive::Arrow> {
self.a(Default::default())
}
pub fn quad(&self) -> Drawing<primitive::Quad> {
self.a(Default::default())
}
pub fn rect(&self) -> Drawing<primitive::Rect> {
self.a(Default::default())
}
pub fn tri(&self) -> Drawing<primitive::Tri> {
self.a(Default::default())
}
pub fn polygon(&self) -> Drawing<primitive::PolygonInit> {
self.a(Default::default())
}
pub fn mesh(&self) -> Drawing<primitive::mesh::Vertexless> {
self.a(Default::default())
}
pub fn polyline(&self) -> Drawing<primitive::PathStroke> {
self.path().stroke()
}
pub fn text(&self, s: &str) -> Drawing<primitive::Text> {
let text = {
let state = self.state.borrow();
let mut intermediary_state = state.intermediary_state.borrow_mut();
let ctxt = DrawingContext::from_intermediary_state(&mut *intermediary_state);
primitive::text::Text::new(ctxt, s)
};
self.a(text)
}
pub fn texture(&self, view: &dyn wgpu::ToTextureView) -> Drawing<primitive::Texture> {
self.a(primitive::Texture::new(view))
}
pub fn drain_commands(&self) -> impl Iterator<Item = DrawCommand> {
self.finish_remaining_drawings();
let cmds = {
let mut state = self.state.borrow_mut();
let empty = Vec::with_capacity(state.draw_commands.len());
std::mem::replace(&mut state.draw_commands, empty)
};
cmds.into_iter().filter_map(|opt| opt)
}
pub fn finish_remaining_drawings(&self) {
self.state.borrow_mut().finish_remaining_drawings()
}
}
impl Default for IntermediaryState {
fn default() -> Self {
let intermediary_mesh = Default::default();
let path_event_buffer = Default::default();
let path_points_colored_buffer = Default::default();
let path_points_textured_buffer = Default::default();
let text_buffer = Default::default();
IntermediaryState {
intermediary_mesh,
path_event_buffer,
path_points_colored_buffer,
path_points_textured_buffer,
text_buffer,
}
}
}
impl Default for State {
fn default() -> Self {
let last_draw_context = None;
let background_color = Default::default();
let draw_commands = Default::default();
let drawing = Default::default();
let intermediary_state = RefCell::new(Default::default());
let theme = Default::default();
State {
last_draw_context,
draw_commands,
drawing,
intermediary_state,
theme,
background_color,
}
}
}
impl Default for Draw {
fn default() -> Self {
let state: Rc<RefCell<State>> = Rc::new(RefCell::new(Default::default()));
let context = Default::default();
Draw { state, context }
}
}
impl Default for Context {
fn default() -> Self {
Self {
transform: Mat4::IDENTITY,
blend: wgpu::BlendState {
color: wgpu::RenderPipelineBuilder::DEFAULT_COLOR_BLEND,
alpha: wgpu::RenderPipelineBuilder::DEFAULT_ALPHA_BLEND,
},
scissor: Scissor::Full,
topology: wgpu::RenderPipelineBuilder::DEFAULT_PRIMITIVE_TOPOLOGY,
sampler: wgpu::SamplerBuilder::new().into_descriptor(),
}
}
}