use std::marker::PhantomData;
use bevy::asset::UntypedAssetId;
use bevy::prelude::*;
use lyon::path::PathEvent;
use lyon::tessellation::{FillOptions, LineCap, LineJoin, StrokeOptions};
use uuid::Uuid;
use crate::draw::primitive::Primitive;
use crate::draw::properties::{
SetColor, SetDimensions, SetFill, SetOrientation, SetPosition, SetStroke, color, fill,
spatial::{dimension, orientation, position},
stroke,
};
use crate::draw::{Draw, DrawCommand, DrawRef};
use crate::render::{DefaultNannouShaderModel, ErasedShaderModel, ShaderModel};
pub struct Drawing<'a, T> {
pub(crate) draw: DrawRef<'a>,
pub(crate) index: usize,
pub(crate) shader_model_index: usize,
pub(crate) finish_on_drop: bool,
_ty: PhantomData<T>,
}
pub struct DrawingContext<'a> {
pub mesh: &'a mut Mesh,
pub path_event_buffer: &'a mut Vec<PathEvent>,
pub path_points_vertex_buffer: &'a mut Vec<(Vec2, Color, Vec2)>,
pub text_buffer: &'a mut String,
}
pub fn new<T>(draw: &Draw, index: usize, model_index: usize) -> Drawing<'_, T>
where
T: Into<Primitive>,
{
let _ty = PhantomData;
let finish_on_drop = true;
Drawing {
draw: DrawRef::Borrowed(draw),
index,
shader_model_index: model_index,
finish_on_drop,
_ty,
}
}
impl<'a, T> Drop for Drawing<'a, T> {
fn drop(&mut self) {
if self.finish_on_drop {
finish_drawing(&self.draw, self.index, self.shader_model_index)
}
}
}
impl<'a> DrawingContext<'a> {
pub(crate) fn from_intermediary_state(state: &'a mut super::IntermediaryState) -> Self {
let super::IntermediaryState {
ref mut intermediary_mesh,
ref mut path_event_buffer,
ref mut path_points_vertex_buffer,
ref mut text_buffer,
} = *state;
DrawingContext {
mesh: intermediary_mesh,
path_event_buffer,
path_points_vertex_buffer,
text_buffer,
}
}
}
impl<'a, T> Drawing<'a, T> {
pub fn finish(mut self) {
self.finish_on_drop = false;
finish_drawing(&self.draw, self.index, self.shader_model_index)
}
pub(crate) fn transition<U>(mut self) -> Drawing<'a, U> {
self.finish_on_drop = false;
let Drawing {
ref draw,
index,
shader_model_index,
..
} = self;
Drawing {
draw: draw.clone(),
index,
shader_model_index,
finish_on_drop: true,
_ty: PhantomData,
}
}
pub fn map_shader_model<SM, F>(self, map: F) -> Drawing<'a, T>
where
SM: ShaderModel,
F: FnOnce(SM) -> SM,
{
let shader_model = {
let state = self.draw.state.read().unwrap();
state.shader_models[&self.draw.shader_model]
.as_any()
.downcast_ref::<SM>()
.cloned()
};
match shader_model {
Some(shader_model) => {
let shader_model = map(shader_model);
self.with_new_shader_model(Box::new(shader_model))
}
None => {
bevy::log::warn_once!(
"`map_shader_model`: the active shader model is not of the requested type; \
the mapping has been skipped"
);
self
}
}
}
fn with_new_shader_model(mut self, model: Box<dyn ErasedShaderModel>) -> Drawing<'a, T> {
self.finish_on_drop = false;
let Drawing {
ref draw,
index,
shader_model_index,
..
} = self;
let new_id = UntypedAssetId::Uuid {
type_id: model.as_any().type_id(),
uuid: Uuid::new_v4(),
};
let state = draw.state.clone();
let mut state = state.write().unwrap();
state.shader_models.insert(new_id.clone(), model);
state.last_shader_model = Some(new_id.clone());
let draw = Draw {
state: draw.state.clone(),
context: draw.context.clone(),
shader_model: new_id.clone(),
window: draw.window,
text_cx: draw.text_cx.clone(),
};
Drawing {
draw: DrawRef::Owned(draw),
index,
shader_model_index,
finish_on_drop: true,
_ty: PhantomData,
}
}
}
impl<'a, T> Drawing<'a, T>
where
T: SetColor,
{
pub fn color<C>(self, color: C) -> Self
where
C: Into<Color>,
{
color::set_color(&self.draw, self.index, color.into());
self
}
pub fn srgb(self, r: f32, g: f32, b: f32) -> Self {
self.color(Color::srgb(r, g, b))
}
pub fn srgb_u8(self, r: u8, g: u8, b: u8) -> Self {
self.color(Color::srgb_u8(r, g, b))
}
pub fn srgba(self, r: f32, g: f32, b: f32, a: f32) -> Self {
self.color(Color::srgba(r, g, b, a))
}
pub fn srgba_u8(self, r: u8, g: u8, b: u8, a: u8) -> Self {
self.color(Color::srgba_u8(r, g, b, a))
}
pub fn linear_rgb(self, r: f32, g: f32, b: f32) -> Self {
self.color(Color::linear_rgb(r, g, b))
}
pub fn linear_rgba(self, r: f32, g: f32, b: f32, a: f32) -> Self {
self.color(Color::linear_rgba(r, g, b, a))
}
pub fn hsl(self, h: f32, s: f32, l: f32) -> Self {
self.color(Color::hsl(h * 360.0, s, l))
}
pub fn hsla(self, h: f32, s: f32, l: f32, a: f32) -> Self {
self.color(Color::hsla(h * 360.0, s, l, a))
}
pub fn hsv(self, h: f32, s: f32, v: f32) -> Self {
self.color(Color::hsv(h * 360.0, s, v))
}
pub fn hsva(self, h: f32, s: f32, v: f32, a: f32) -> Self {
self.color(Color::hsva(h * 360.0, s, v, a))
}
pub fn hwb(self, h: f32, w: f32, b: f32) -> Self {
self.color(Color::hwb(h * 360.0, w, b))
}
pub fn hwba(self, h: f32, w: f32, b: f32, a: f32) -> Self {
self.color(Color::hwba(h * 360.0, w, b, a))
}
pub fn lab(self, l: f32, a: f32, b: f32) -> Self {
self.color(Color::lab(l, a, b))
}
pub fn laba(self, l: f32, a: f32, b: f32, alpha: f32) -> Self {
self.color(Color::laba(l, a, b, alpha))
}
pub fn lch(self, l: f32, c: f32, h: f32) -> Self {
self.color(Color::lch(l, c, h))
}
pub fn lcha(self, l: f32, c: f32, h: f32, alpha: f32) -> Self {
self.color(Color::lcha(l, c, h, alpha))
}
pub fn oklab(self, l: f32, a: f32, b: f32) -> Self {
self.color(Color::oklab(l, a, b))
}
pub fn oklaba(self, l: f32, a: f32, b: f32, alpha: f32) -> Self {
self.color(Color::oklaba(l, a, b, alpha))
}
pub fn oklch(self, l: f32, c: f32, h: f32) -> Self {
self.color(Color::oklch(l, c, h))
}
pub fn oklcha(self, l: f32, c: f32, h: f32, alpha: f32) -> Self {
self.color(Color::oklcha(l, c, h, alpha))
}
pub fn cie_xyz(self, x: f32, y: f32, z: f32) -> Self {
self.color(Color::xyz(x, y, z))
}
pub fn cie_xyza(self, x: f32, y: f32, z: f32, alpha: f32) -> Self {
self.color(Color::xyza(x, y, z, alpha))
}
pub fn gray(self, g: f32) -> Self {
self.color(Color::srgb(g, g, g))
}
}
impl<'a, T> Drawing<'a, T>
where
T: SetDimensions,
{
pub fn width(self, w: f32) -> Self {
dimension::set_dimensions(&self.draw, self.index, Some(w), None, None);
self
}
pub fn height(self, h: f32) -> Self {
dimension::set_dimensions(&self.draw, self.index, None, Some(h), None);
self
}
pub fn depth(self, d: f32) -> Self {
dimension::set_dimensions(&self.draw, self.index, None, None, Some(d));
self
}
pub fn w(self, w: f32) -> Self {
self.width(w)
}
pub fn h(self, h: f32) -> Self {
self.height(h)
}
pub fn d(self, d: f32) -> Self {
self.depth(d)
}
pub fn wh(self, v: Vec2) -> Self {
dimension::set_dimensions(&self.draw, self.index, Some(v.x), Some(v.y), None);
self
}
pub fn whd(self, v: Vec3) -> Self {
dimension::set_dimensions(&self.draw, self.index, Some(v.x), Some(v.y), Some(v.z));
self
}
pub fn w_h(self, x: f32, y: f32) -> Self {
dimension::set_dimensions(&self.draw, self.index, Some(x), Some(y), None);
self
}
pub fn w_h_d(self, x: f32, y: f32, z: f32) -> Self {
dimension::set_dimensions(&self.draw, self.index, Some(x), Some(y), Some(z));
self
}
}
impl<'a, T> Drawing<'a, T>
where
T: SetPosition,
{
pub fn x(self, x: f32) -> Self {
position::set_position(&self.draw, self.index, Some(x), None, None);
self
}
pub fn y(self, y: f32) -> Self {
position::set_position(&self.draw, self.index, None, Some(y), None);
self
}
pub fn z(self, z: f32) -> Self {
position::set_position(&self.draw, self.index, None, None, Some(z));
self
}
pub fn xy(self, p: Vec2) -> Self {
position::set_position(&self.draw, self.index, Some(p.x), Some(p.y), None);
self
}
pub fn xyz(self, p: Vec3) -> Self {
position::set_position(&self.draw, self.index, Some(p.x), Some(p.y), Some(p.z));
self
}
pub fn x_y(self, x: f32, y: f32) -> Self {
position::set_position(&self.draw, self.index, Some(x), Some(y), None);
self
}
pub fn x_y_z(self, x: f32, y: f32, z: f32) -> Self {
position::set_position(&self.draw, self.index, Some(x), Some(y), Some(z));
self
}
}
impl<'a, T> Drawing<'a, T>
where
T: SetOrientation,
{
pub fn look_at(self, target: Vec3) -> Self {
orientation::set_orientation(&self.draw, self.index, orientation::Update::LookAt(target));
self
}
pub fn x_radians(self, x: f32) -> Self {
orientation::set_orientation(&self.draw, self.index, orientation::Update::XRadians(x));
self
}
pub fn y_radians(self, y: f32) -> Self {
orientation::set_orientation(&self.draw, self.index, orientation::Update::YRadians(y));
self
}
pub fn z_radians(self, z: f32) -> Self {
orientation::set_orientation(&self.draw, self.index, orientation::Update::ZRadians(z));
self
}
pub fn x_degrees(self, x: f32) -> Self {
self.x_radians(x.to_radians())
}
pub fn y_degrees(self, y: f32) -> Self {
self.y_radians(y.to_radians())
}
pub fn z_degrees(self, z: f32) -> Self {
self.z_radians(z.to_radians())
}
pub fn x_turns(self, x: f32) -> Self {
self.x_radians(x * std::f32::consts::TAU)
}
pub fn y_turns(self, y: f32) -> Self {
self.y_radians(y * std::f32::consts::TAU)
}
pub fn z_turns(self, z: f32) -> Self {
self.z_radians(z * std::f32::consts::TAU)
}
pub fn radians(self, v: Vec3) -> Self {
orientation::set_orientation(&self.draw, self.index, orientation::Update::Radians(v));
self
}
pub fn degrees(self, v: Vec3) -> Self {
self.radians(Vec3::new(
v.x.to_radians(),
v.y.to_radians(),
v.z.to_radians(),
))
}
pub fn turns(self, v: Vec3) -> Self {
self.radians(v * std::f32::consts::TAU)
}
pub fn euler(self, e: Vec3) -> Self {
self.radians(e)
}
pub fn quaternion(self, q: Quat) -> Self {
orientation::set_orientation(&self.draw, self.index, orientation::Update::Quat(q));
self
}
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)
}
}
impl<'a, T> Drawing<'a, T>
where
T: SetFill,
{
pub fn fill_opts(self, opts: FillOptions) -> Self {
fill::set_fill(&self.draw, self.index, fill::Update::Opts(opts));
self
}
pub fn fill_tolerance(self, tolerance: f32) -> Self {
fill::set_fill(&self.draw, self.index, fill::Update::Tolerance(tolerance));
self
}
pub fn fill_rule(self, rule: lyon::tessellation::FillRule) -> Self {
fill::set_fill(&self.draw, self.index, fill::Update::Rule(rule));
self
}
pub fn fill_sweep_orientation(self, orientation: lyon::tessellation::Orientation) -> Self {
fill::set_fill(
&self.draw,
self.index,
fill::Update::SweepOrientation(orientation),
);
self
}
pub fn handle_intersections(self, b: bool) -> Self {
fill::set_fill(&self.draw, self.index, fill::Update::HandleIntersections(b));
self
}
}
impl<'a, T> Drawing<'a, T>
where
T: SetStroke,
{
pub fn start_cap(self, cap: LineCap) -> Self {
stroke::set_stroke(&self.draw, self.index, stroke::Update::StartCap(cap));
self
}
pub fn end_cap(self, cap: LineCap) -> Self {
stroke::set_stroke(&self.draw, self.index, stroke::Update::EndCap(cap));
self
}
pub fn caps(self, cap: LineCap) -> Self {
stroke::set_stroke(&self.draw, self.index, stroke::Update::Caps(cap));
self
}
pub fn start_cap_butt(self) -> Self {
self.start_cap(LineCap::Butt)
}
pub fn start_cap_square(self) -> Self {
self.start_cap(LineCap::Square)
}
pub fn start_cap_round(self) -> Self {
self.start_cap(LineCap::Round)
}
pub fn end_cap_butt(self) -> Self {
self.end_cap(LineCap::Butt)
}
pub fn end_cap_square(self) -> Self {
self.end_cap(LineCap::Square)
}
pub fn end_cap_round(self) -> Self {
self.end_cap(LineCap::Round)
}
pub fn caps_butt(self) -> Self {
self.caps(LineCap::Butt)
}
pub fn caps_square(self) -> Self {
self.caps(LineCap::Square)
}
pub fn caps_round(self) -> Self {
self.caps(LineCap::Round)
}
pub fn join(self, join: LineJoin) -> Self {
stroke::set_stroke(&self.draw, self.index, stroke::Update::Join(join));
self
}
pub fn join_miter(self) -> Self {
self.join(LineJoin::Miter)
}
pub fn join_miter_clip(self) -> Self {
self.join(LineJoin::MiterClip)
}
pub fn join_round(self) -> Self {
self.join(LineJoin::Round)
}
pub fn join_bevel(self) -> Self {
self.join(LineJoin::Bevel)
}
pub fn stroke_weight(self, stroke_weight: f32) -> Self {
stroke::set_stroke(
&self.draw,
self.index,
stroke::Update::Weight(stroke_weight),
);
self
}
pub fn miter_limit(self, limit: f32) -> Self {
stroke::set_stroke(&self.draw, self.index, stroke::Update::MiterLimit(limit));
self
}
pub fn stroke_tolerance(self, tolerance: f32) -> Self {
stroke::set_stroke(&self.draw, self.index, stroke::Update::Tolerance(tolerance));
self
}
pub fn stroke_opts(self, opts: StrokeOptions) -> Self {
stroke::set_stroke(&self.draw, self.index, stroke::Update::Opts(opts));
self
}
}
impl<'a, T> Drawing<'a, T> {
pub fn base_color<C: Into<Color>>(self, color: C) -> Self {
let color = color.into();
self.map_shader_model(|mut model: DefaultNannouShaderModel| {
model.color = color;
model
})
}
pub fn texture(self, texture: &Handle<Image>) -> Self {
let mut model = {
let state = self.draw.state.read().unwrap();
state.shader_models[&self.draw.shader_model].clone_erased()
};
model.set_texture_erased(texture.clone());
self.with_new_shader_model(model)
}
}
fn finish_drawing(draw: &DrawRef, index: usize, shader_model_index: usize) {
match draw.state.try_write() {
Err(err) => eprintln!("drawing failed to borrow state and finish: {}", err),
Ok(mut state) => {
if let DrawRef::Owned(draw) = draw {
let id = draw.shader_model.clone();
let shader_model_cmd = state
.draw_commands
.get_mut(shader_model_index)
.expect("expected a valid shader model index");
if shader_model_cmd.is_none() {
*shader_model_cmd = Some(DrawCommand::ShaderModel(id));
}
}
state.finish_drawing(index);
}
}
}
pub(crate) fn with_primitive(draw: &Draw, index: usize, f: impl FnOnce(&mut Primitive)) {
if let Ok(mut state) = draw.state.try_write() {
if let Some(primitive) = state.drawing.get_mut(&index) {
f(primitive);
}
}
}
pub(crate) fn with_primitive_ctxt(
draw: &Draw,
index: usize,
f: impl FnOnce(Primitive, DrawingContext) -> Primitive,
) {
if let Ok(mut state) = draw.state.try_write() {
let state = &mut *state;
if let Some(primitive) = state.drawing.get_mut(&index) {
let prim = std::mem::take(primitive);
let new = {
let mut intermediary_state = state.intermediary_state.write().unwrap();
let ctxt = DrawingContext::from_intermediary_state(&mut intermediary_state);
f(prim, ctxt)
};
*primitive = new;
}
}
}