use crate::color::conv::IntoLinSrgba;
use crate::draw::drawing::DrawingContext;
use crate::draw::mesh::vertex::TexCoords;
use crate::draw::primitive::path::{self, PathEventSource};
use crate::draw::primitive::Primitive;
use crate::draw::properties::spatial::{orientation, position};
use crate::draw::properties::{
ColorScalar, LinSrgba, SetColor, SetOrientation, SetPosition, SetStroke,
};
use crate::draw::{self, Drawing};
use crate::geom::Point2;
use crate::wgpu;
use lyon::path::PathEvent;
use lyon::tessellation::StrokeOptions;
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: IntoLinSrgba<ColorScalar>,
{
self.polygon_options_mut().stroke_color = Some(color.into_lin_srgba());
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<LinSrgba>,
pub color: Option<LinSrgba>,
pub stroke: Option<StrokeOptions>,
}
#[derive(Clone, Debug)]
pub struct Polygon {
opts: PolygonOptions,
path_event_src: PathEventSource,
texture_view: Option<wgpu::TextureView>,
}
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: IntoLinSrgba<ColorScalar>,
{
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),
texture_view: None,
}
}
pub fn points<I>(self, ctxt: DrawingContext, points: I) -> Polygon
where
I: IntoIterator,
I::Item: Into<Point2>,
{
let points = points.into_iter().map(|p| {
let p: Point2 = 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_colored<I, P, C>(self, ctxt: DrawingContext, points: I) -> Polygon
where
I: IntoIterator<Item = (P, C)>,
P: Into<Point2>,
C: IntoLinSrgba<ColorScalar>,
{
let DrawingContext {
path_points_colored_buffer,
..
} = ctxt;
let start = path_points_colored_buffer.len();
let points = points
.into_iter()
.map(|(p, c)| (p.into(), c.into_lin_srgba()));
path_points_colored_buffer.extend(points);
let end = path_points_colored_buffer.len();
Polygon {
opts: self.opts,
path_event_src: PathEventSource::ColoredPoints {
range: start..end,
close: true,
},
texture_view: None,
}
}
pub fn points_textured<I, P, T>(
self,
ctxt: DrawingContext,
view: &dyn wgpu::ToTextureView,
points: I,
) -> Polygon
where
I: IntoIterator<Item = (P, T)>,
P: Into<Point2>,
T: Into<TexCoords>,
{
let DrawingContext {
path_points_textured_buffer,
..
} = ctxt;
let start = path_points_textured_buffer.len();
let points = points.into_iter().map(|(p, c)| (p.into(), c.into()));
path_points_textured_buffer.extend(points);
let end = path_points_textured_buffer.len();
Polygon {
opts: self.opts,
path_event_src: PathEventSource::TexturedPoints {
range: start..end,
close: true,
},
texture_view: Some(view.to_texture_view()),
}
}
}
pub fn render_events_themed<F, I>(
opts: PolygonOptions,
events: F,
mut ctxt: draw::renderer::RenderContext,
theme_primitive: &draw::theme::Primitive,
mesh: &mut draw::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<LinSrgba>,
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,
points: I,
ctxt: draw::renderer::RenderContext,
theme_primitive: &draw::theme::Primitive,
mesh: &mut draw::Mesh,
) where
I: Clone + Iterator<Item = Point2>,
{
render_events_themed(
opts,
|| lyon::path::iterator::FromPolyline::closed(points.clone().map(|p| p.to_array().into())),
ctxt,
theme_primitive,
mesh,
);
}
impl Polygon {
pub(crate) fn render_themed(
self,
ctxt: draw::renderer::RenderContext,
mesh: &mut draw::Mesh,
theme_primitive: &draw::theme::Primitive,
) -> draw::renderer::PrimitiveRender {
let Polygon {
path_event_src,
opts:
PolygonOptions {
position,
orientation,
no_fill,
stroke_color,
color,
stroke,
},
texture_view,
} = self;
let draw::renderer::RenderContext {
fill_tessellator,
stroke_tessellator,
path_event_buffer,
path_points_colored_buffer,
path_points_textured_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<LinSrgba>,
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::ColoredPoints { ref range, close } => {
let mut points_colored =
path_points_colored_buffer[range.clone()].iter().cloned();
let src = path::PathEventSourceIter::ColoredPoints {
points: &mut points_colored,
close,
};
render(
src,
opts,
color,
theme,
fill_tessellator,
stroke_tessellator,
);
}
PathEventSource::TexturedPoints { ref range, close } => {
let mut textured_points =
path_points_textured_buffer[range.clone()].iter().cloned();
let src = path::PathEventSourceIter::TexturedPoints {
points: &mut textured_points,
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::ColoredPoints { range, close } => {
let color =
stroke_color.unwrap_or_else(|| theme.stroke_lin_srgba(theme_primitive));
let mut points_colored = path_points_colored_buffer[range]
.iter()
.cloned()
.map(|(point, _)| (point, color));
let src = path::PathEventSourceIter::ColoredPoints {
points: &mut points_colored,
close,
};
render(
src,
opts,
stroke_color,
theme,
fill_tessellator,
stroke_tessellator,
);
}
PathEventSource::TexturedPoints { range, close } => {
let mut textured_points = path_points_textured_buffer[range].iter().cloned();
let src = path::PathEventSourceIter::TexturedPoints {
points: &mut textured_points,
close,
};
render(
src,
opts,
stroke_color,
theme,
fill_tessellator,
stroke_tessellator,
);
}
}
}
match texture_view {
None => draw::renderer::PrimitiveRender::default(),
Some(texture_view) => draw::renderer::PrimitiveRender {
texture_view: Some(texture_view),
vertex_mode: draw::renderer::VertexMode::Texture,
},
}
}
}
impl draw::renderer::RenderPrimitive for Polygon {
fn render_primitive(
self,
ctxt: draw::renderer::RenderContext,
mesh: &mut draw::Mesh,
) -> draw::renderer::PrimitiveRender {
self.render_themed(ctxt, mesh, &draw::theme::Primitive::Polygon)
}
}
impl<'a, T> Drawing<'a, T>
where
T: SetPolygon + Into<Primitive>,
Primitive: Into<Option<T>>,
{
pub fn no_fill(self) -> Self {
self.map_ty(|ty| ty.no_fill())
}
pub fn stroke_color<C>(self, color: C) -> Self
where
C: IntoLinSrgba<ColorScalar>,
{
self.map_ty(|ty| ty.stroke_color(color))
}
pub fn polygon_options(self, opts: PolygonOptions) -> Self {
self.map_ty(|ty| ty.polygon_options(opts))
}
}
impl<'a> DrawingPolygonInit<'a> {
pub fn stroke<C>(self, color: C) -> Self
where
C: IntoLinSrgba<ColorScalar>,
{
self.map_ty(|ty| ty.stroke(color))
}
pub fn events<I>(self, events: I) -> DrawingPolygon<'a>
where
I: IntoIterator<Item = PathEvent>,
{
self.map_ty_with_context(|ty, ctxt| ty.events(ctxt, events))
}
pub fn points<I>(self, points: I) -> DrawingPolygon<'a>
where
I: IntoIterator,
I::Item: Into<Point2>,
{
self.map_ty_with_context(|ty, ctxt| ty.points(ctxt, points))
}
pub fn points_colored<I, P, C>(self, points: I) -> DrawingPolygon<'a>
where
I: IntoIterator<Item = (P, C)>,
P: Into<Point2>,
C: IntoLinSrgba<ColorScalar>,
{
self.map_ty_with_context(|ty, ctxt| ty.points_colored(ctxt, points))
}
pub fn points_textured<I, P, T>(
self,
view: &dyn wgpu::ToTextureView,
points: I,
) -> DrawingPolygon<'a>
where
I: IntoIterator<Item = (P, T)>,
P: Into<Point2>,
T: Into<TexCoords>,
{
self.map_ty_with_context(|ty, ctxt| ty.points_textured(ctxt, view, points))
}
}
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<ColorScalar> for PolygonInit {
fn rgba_mut(&mut self) -> &mut Option<LinSrgba> {
SetColor::rgba_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<ColorScalar> for Polygon {
fn rgba_mut(&mut self) -> &mut Option<LinSrgba> {
SetColor::rgba_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)
}
}
impl Into<Option<PolygonInit>> for Primitive {
fn into(self) -> Option<PolygonInit> {
match self {
Primitive::PolygonInit(prim) => Some(prim),
_ => None,
}
}
}
impl Into<Option<Polygon>> for Primitive {
fn into(self) -> Option<Polygon> {
match self {
Primitive::Polygon(prim) => Some(prim),
_ => None,
}
}
}