use bevy::prelude::*;
use lyon::path::PathEvent;
use lyon::tessellation::StrokeOptions;
use crate::draw::drawing::{self, DrawingContext};
use crate::draw::primitive::Primitive;
use crate::draw::primitive::path::{self, PathEventSource};
use crate::draw::properties::spatial::{orientation, position};
use crate::draw::properties::{SetColor, SetOrientation, SetPosition, SetStroke};
use crate::draw::{self, Drawing};
pub trait SetPolygon: Sized {
fn polygon_options_mut(&mut self) -> &mut PolygonOptions;
fn no_fill(mut self) -> Self {
self.polygon_options_mut().no_fill = true;
self
}
fn stroke_color<C>(mut self, color: C) -> Self
where
C: Into<Color>,
{
self.polygon_options_mut().stroke_color = Some(color.into());
self
}
fn polygon_options(mut self, opts: PolygonOptions) -> Self {
*self.polygon_options_mut() = opts;
self
}
}
#[derive(Clone, Debug, Default)]
pub struct PolygonInit {
pub(crate) opts: PolygonOptions,
}
#[derive(Clone, Debug, Default)]
pub struct PolygonOptions {
pub position: position::Properties,
pub orientation: orientation::Properties,
pub no_fill: bool,
pub stroke_color: Option<Color>,
pub color: Option<Color>,
pub stroke: Option<StrokeOptions>,
}
#[derive(Clone, Debug)]
pub struct Polygon {
opts: PolygonOptions,
path_event_src: PathEventSource,
}
pub type DrawingPolygonInit<'a> = Drawing<'a, PolygonInit>;
pub type DrawingPolygon<'a> = Drawing<'a, Polygon>;
impl PolygonInit {
pub fn stroke<C>(self, color: C) -> Self
where
C: Into<Color>,
{
self.stroke_color(color)
}
pub(crate) fn events<I>(self, ctxt: DrawingContext, events: I) -> Polygon
where
I: IntoIterator<Item = PathEvent>,
{
let DrawingContext {
path_event_buffer, ..
} = ctxt;
let start = path_event_buffer.len();
path_event_buffer.extend(events);
let end = path_event_buffer.len();
Polygon {
opts: self.opts,
path_event_src: PathEventSource::Buffered(start..end),
}
}
pub fn points<I>(self, ctxt: DrawingContext, points: I) -> Polygon
where
I: IntoIterator,
I::Item: Into<Vec2>,
{
let points = points.into_iter().map(|p| {
let p: Vec2 = p.into();
p.to_array().into()
});
let close = true;
let events = lyon::path::iterator::FromPolyline::new(close, points);
self.events(ctxt, events)
}
pub fn points_vertex<I, P, C, U>(self, ctxt: DrawingContext, points: I) -> Polygon
where
I: IntoIterator<Item = (P, C, U)>,
P: Into<Vec2>,
C: Into<Color>,
U: Into<Vec2>,
{
let DrawingContext {
path_points_vertex_buffer: path_points_colored_buffer,
..
} = ctxt;
let start = path_points_colored_buffer.len();
let points = points
.into_iter()
.map(|(p, c, u)| (p.into(), c.into(), u.into()));
path_points_colored_buffer.extend(points);
let end = path_points_colored_buffer.len();
Polygon {
opts: self.opts,
path_event_src: PathEventSource::Vertex {
range: start..end,
close: true,
},
}
}
}
pub fn render_events_themed<F, I>(
opts: PolygonOptions,
events: F,
mut ctxt: draw::render::RenderContext,
theme_primitive: &draw::theme::Primitive,
mesh: &mut Mesh,
) where
F: Fn() -> I,
I: Iterator<Item = lyon::path::PathEvent>,
{
let PolygonOptions {
position,
orientation,
no_fill,
stroke_color,
color,
stroke,
} = opts;
let global_transform = *ctxt.transform;
let local_transform = position.transform() * orientation.transform();
let transform = global_transform * local_transform;
let mut render =
|opts: path::Options,
color: Option<Color>,
theme: &draw::Theme,
fill_tessellator: &mut lyon::tessellation::FillTessellator,
stroke_tessellator: &mut lyon::tessellation::StrokeTessellator| {
path::render_path_events(
events(),
color,
transform,
opts,
theme,
theme_primitive,
fill_tessellator,
stroke_tessellator,
mesh,
)
};
if !no_fill {
let opts = path::Options::Fill(lyon::tessellation::FillOptions::default());
render(
opts,
color,
&ctxt.theme,
&mut ctxt.fill_tessellator,
&mut ctxt.stroke_tessellator,
);
}
if let Some(stroke_opts) = stroke {
let opts = path::Options::Stroke(stroke_opts);
let color = stroke_color;
render(
opts,
color,
&ctxt.theme,
&mut ctxt.fill_tessellator,
&mut ctxt.stroke_tessellator,
);
}
}
pub fn render_points_themed<I>(
opts: PolygonOptions,
close: bool,
points: I,
mut ctxt: draw::render::RenderContext,
theme_primitive: &draw::theme::Primitive,
mesh: &mut Mesh,
) where
I: Clone + Iterator<Item = (Vec2, Vec2)>,
{
let PolygonOptions {
position,
orientation,
no_fill,
stroke_color,
color,
stroke,
} = opts;
let global_transform = *ctxt.transform;
let local_transform = position.transform() * orientation.transform();
let transform = global_transform * local_transform;
let mut render =
|opts: path::Options,
color: Option<Color>,
theme: &draw::Theme,
fill_tessellator: &mut lyon::tessellation::FillTessellator,
stroke_tessellator: &mut lyon::tessellation::StrokeTessellator| {
path::render_path_points_themed(
points.clone(),
close,
color,
transform,
opts,
theme,
theme_primitive,
fill_tessellator,
stroke_tessellator,
mesh,
)
};
if !no_fill {
let opts = path::Options::Fill(lyon::tessellation::FillOptions::default());
render(
opts,
color,
&ctxt.theme,
&mut ctxt.fill_tessellator,
&mut ctxt.stroke_tessellator,
);
}
if let Some(stroke_opts) = stroke {
let opts = path::Options::Stroke(stroke_opts);
let color = stroke_color;
render(
opts,
color,
&ctxt.theme,
&mut ctxt.fill_tessellator,
&mut ctxt.stroke_tessellator,
);
}
}
impl Polygon {
pub(crate) fn render_themed(
self,
ctxt: draw::render::RenderContext,
mesh: &mut Mesh,
theme_primitive: &draw::theme::Primitive,
) {
let Polygon {
path_event_src,
opts:
PolygonOptions {
position,
orientation,
no_fill,
stroke_color,
color,
stroke,
},
} = self;
let draw::render::RenderContext {
fill_tessellator,
stroke_tessellator,
path_event_buffer,
path_points_vertex_buffer,
transform,
theme,
..
} = ctxt;
let global_transform = *transform;
let local_transform = position.transform() * orientation.transform();
let transform = global_transform * local_transform;
let mut render =
|src: path::PathEventSourceIter,
opts: path::Options,
color: Option<Color>,
theme: &draw::Theme,
fill_tessellator: &mut lyon::tessellation::FillTessellator,
stroke_tessellator: &mut lyon::tessellation::StrokeTessellator| {
path::render_path_source(
src,
color,
transform,
opts,
theme,
theme_primitive,
fill_tessellator,
stroke_tessellator,
mesh,
)
};
if !no_fill {
let opts = path::Options::Fill(lyon::tessellation::FillOptions::default());
match path_event_src {
PathEventSource::Buffered(ref range) => {
let mut events = path_event_buffer[range.clone()].iter().cloned();
let src = path::PathEventSourceIter::Events(&mut events);
render(
src,
opts,
color,
theme,
fill_tessellator,
stroke_tessellator,
);
}
PathEventSource::Vertex { ref range, close } => {
let mut points_colored =
path_points_vertex_buffer[range.clone()].iter().cloned();
let src = path::PathEventSourceIter::Vertex {
points: &mut points_colored,
close,
};
render(
src,
opts,
color,
theme,
fill_tessellator,
stroke_tessellator,
);
}
}
}
if let Some(stroke_opts) = stroke {
let opts = path::Options::Stroke(stroke_opts);
match path_event_src {
PathEventSource::Buffered(range) => {
let mut events = path_event_buffer[range].iter().cloned();
let src = path::PathEventSourceIter::Events(&mut events);
render(
src,
opts,
stroke_color,
theme,
fill_tessellator,
stroke_tessellator,
);
}
PathEventSource::Vertex { range, close } => {
let color = stroke_color.unwrap_or_else(|| theme.stroke(theme_primitive));
let mut points_vertex = path_points_vertex_buffer[range]
.iter()
.cloned()
.map(|(point, _, tex_coord)| (point, color, tex_coord));
let src = path::PathEventSourceIter::Vertex {
points: &mut points_vertex,
close,
};
render(
src,
opts,
stroke_color,
theme,
fill_tessellator,
stroke_tessellator,
);
}
}
}
}
}
impl draw::render::RenderPrimitive for Polygon {
fn render_primitive(self, ctxt: draw::render::RenderContext, mesh: &mut Mesh) {
self.render_themed(ctxt, mesh, &draw::theme::Primitive::Polygon)
}
}
impl<'a, T> Drawing<'a, T>
where
T: SetPolygon,
{
pub fn no_fill(self) -> Self {
set_polygon(&self.draw, self.index, Update::NoFill);
self
}
pub fn stroke_color<C>(self, color: C) -> Self
where
C: Into<Color>,
{
set_polygon(&self.draw, self.index, Update::StrokeColor(color.into()));
self
}
pub fn polygon_options(self, opts: PolygonOptions) -> Self {
set_polygon(&self.draw, self.index, Update::Opts(opts));
self
}
}
impl<'a> DrawingPolygonInit<'a> {
pub fn stroke<C>(self, color: C) -> Self
where
C: Into<Color>,
{
self.stroke_color(color)
}
pub fn events<I>(self, events: I) -> DrawingPolygon<'a>
where
I: IntoIterator<Item = PathEvent>,
{
let mut events = events.into_iter();
polygon_events(&self.draw, self.index, &mut events);
self.transition()
}
pub fn points<I>(self, points: I) -> DrawingPolygon<'a>
where
I: IntoIterator,
I::Item: Into<Vec2>,
{
let mut points = points.into_iter().map(Into::into);
polygon_points(&self.draw, self.index, &mut points);
self.transition()
}
pub fn points_colored<I, P, C>(self, points: I) -> DrawingPolygon<'a>
where
I: IntoIterator<Item = (P, C)>,
P: Into<Vec2>,
C: Into<Color>,
{
let mut points = points
.into_iter()
.map(|(p, c)| (p.into(), c.into(), Vec2::ZERO));
polygon_points_vertex(&self.draw, self.index, &mut points);
self.transition()
}
pub fn points_vertex<I, P, C, U>(self, points: I) -> DrawingPolygon<'a>
where
I: IntoIterator<Item = (P, C, U)>,
P: Into<Vec2>,
C: Into<Color>,
U: Into<Vec2>,
{
let mut points = points
.into_iter()
.map(|(p, c, u)| (p.into(), c.into(), u.into()));
polygon_points_vertex(&self.draw, self.index, &mut points);
self.transition()
}
}
impl SetPolygon for PolygonOptions {
fn polygon_options_mut(&mut self) -> &mut PolygonOptions {
self
}
}
impl SetOrientation for PolygonInit {
fn properties(&mut self) -> &mut orientation::Properties {
SetOrientation::properties(&mut self.opts.orientation)
}
}
impl SetPosition for PolygonInit {
fn properties(&mut self) -> &mut position::Properties {
SetPosition::properties(&mut self.opts.position)
}
}
impl SetColor for PolygonInit {
fn color_mut(&mut self) -> &mut Option<Color> {
SetColor::color_mut(&mut self.opts.color)
}
}
impl SetPolygon for PolygonInit {
fn polygon_options_mut(&mut self) -> &mut PolygonOptions {
SetPolygon::polygon_options_mut(&mut self.opts)
}
}
impl SetStroke for PolygonInit {
fn stroke_options_mut(&mut self) -> &mut StrokeOptions {
SetStroke::stroke_options_mut(&mut self.opts.stroke)
}
}
impl SetOrientation for Polygon {
fn properties(&mut self) -> &mut orientation::Properties {
SetOrientation::properties(&mut self.opts.orientation)
}
}
impl SetPosition for Polygon {
fn properties(&mut self) -> &mut position::Properties {
SetPosition::properties(&mut self.opts.position)
}
}
impl SetColor for Polygon {
fn color_mut(&mut self) -> &mut Option<Color> {
SetColor::color_mut(&mut self.opts.color)
}
}
impl From<PolygonInit> for Primitive {
fn from(prim: PolygonInit) -> Self {
Primitive::PolygonInit(prim)
}
}
impl From<Polygon> for Primitive {
fn from(prim: Polygon) -> Self {
Primitive::Polygon(prim)
}
}
pub(crate) enum Update {
NoFill,
StrokeColor(Color),
Opts(PolygonOptions),
}
pub(crate) fn set_polygon(draw: &draw::Draw, index: usize, update: Update) {
drawing::with_primitive(draw, index, |prim| match prim.polygon_options_mut() {
Some(opts) => match update {
Update::NoFill => opts.no_fill = true,
Update::StrokeColor(color) => opts.stroke_color = Some(color),
Update::Opts(new) => *opts = new,
},
None => bevy::log::warn_once!("drawing primitive does not support `polygon` options"),
})
}
fn polygon_events(draw: &draw::Draw, index: usize, events: &mut dyn Iterator<Item = PathEvent>) {
drawing::with_primitive_ctxt(draw, index, |prim, ctxt| match prim {
Primitive::PolygonInit(p) => Primitive::Polygon(p.events(ctxt, events)),
other => {
bevy::log::warn_once!("expected a `PolygonInit` primitive");
other
}
})
}
fn polygon_points(draw: &draw::Draw, index: usize, points: &mut dyn Iterator<Item = Vec2>) {
drawing::with_primitive_ctxt(draw, index, |prim, ctxt| match prim {
Primitive::PolygonInit(p) => Primitive::Polygon(p.points(ctxt, points)),
other => {
bevy::log::warn_once!("expected a `PolygonInit` primitive");
other
}
})
}
fn polygon_points_vertex(
draw: &draw::Draw,
index: usize,
points: &mut dyn Iterator<Item = (Vec2, Color, Vec2)>,
) {
drawing::with_primitive_ctxt(draw, index, |prim, ctxt| match prim {
Primitive::PolygonInit(p) => Primitive::Polygon(p.points_vertex(ctxt, points)),
other => {
bevy::log::warn_once!("expected a `PolygonInit` primitive");
other
}
})
}