use bevy::prelude::*;
use lyon::path::PathEvent;
use lyon::tessellation::{FillOptions, FillTessellator, StrokeOptions, StrokeTessellator};
use crate::draw::primitive::Primitive;
use crate::draw::properties::spatial::{orientation, position};
use crate::draw::properties::{SetColor, SetFill, SetOrientation, SetPosition, SetStroke};
use crate::draw::{self, Drawing, DrawingContext, drawing};
pub trait TessellationOptions {
type Tessellator;
fn into_options(self) -> Options;
}
#[derive(Clone, Debug)]
pub(crate) enum PathEventSource {
Buffered(std::ops::Range<usize>),
Vertex {
range: std::ops::Range<usize>,
close: bool,
},
}
pub(crate) enum PathEventSourceIter<'a> {
Events(&'a mut dyn Iterator<Item = PathEvent>),
Vertex {
points: &'a mut dyn Iterator<Item = (Vec2, Color, Vec2)>,
close: bool,
},
}
#[derive(Clone, Debug, Default)]
pub struct PathInit;
#[derive(Clone, Debug, Default)]
pub struct PathOptions<T> {
pub(crate) opts: T,
pub(crate) color: Option<Color>,
pub(crate) position: position::Properties,
pub(crate) orientation: orientation::Properties,
}
pub struct Tessellators<'a> {
pub fill: &'a mut FillTessellator,
pub stroke: &'a mut StrokeTessellator,
}
pub type PathFill = PathOptions<FillOptions>;
pub type PathStroke = PathOptions<StrokeOptions>;
#[derive(Clone, Debug)]
pub struct Path {
color: Option<Color>,
position: position::Properties,
orientation: orientation::Properties,
path_event_src: PathEventSource,
options: Options,
}
pub type DrawingPathInit<'a> = Drawing<'a, PathInit>;
pub type DrawingPathOptions<'a, T> = Drawing<'a, PathOptions<T>>;
pub type DrawingPathStroke<'a> = Drawing<'a, PathStroke>;
pub type DrawingPathFill<'a> = Drawing<'a, PathFill>;
pub type DrawingPath<'a> = Drawing<'a, Path>;
#[derive(Clone, Debug)]
pub enum Options {
Fill(FillOptions),
Stroke(StrokeOptions),
}
impl PathInit {
pub fn fill(self) -> PathFill {
let opts = FillOptions::default();
PathFill::new(opts)
}
pub fn stroke(self) -> PathStroke {
let opts = Default::default();
PathStroke::new(opts)
}
}
impl<T> PathOptions<T> {
pub fn new(opts: T) -> Self {
let orientation = Default::default();
let position = Default::default();
let color = Default::default();
PathOptions {
opts,
orientation,
position,
color,
}
}
}
impl PathFill {
pub fn tolerance(self, tolerance: f32) -> Self {
self.fill_tolerance(tolerance)
}
pub fn rule(self, rule: lyon::tessellation::FillRule) -> Self {
self.fill_rule(rule)
}
}
impl PathStroke {
pub fn weight(self, weight: f32) -> Self {
self.stroke_weight(weight)
}
pub fn tolerance(self, tolerance: f32) -> Self {
self.stroke_tolerance(tolerance)
}
}
impl<T> PathOptions<T>
where
T: TessellationOptions,
{
pub(crate) fn events<I>(self, ctxt: DrawingContext, events: I) -> Path
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();
Path::new(
self.position,
self.orientation,
self.color,
PathEventSource::Buffered(start..end),
self.opts.into_options(),
)
}
pub fn points<I>(self, ctxt: DrawingContext, points: I) -> Path
where
I: IntoIterator,
I::Item: Into<Vec2>,
{
self.points_inner(ctxt, false, points)
}
pub fn points_closed<I>(self, ctxt: DrawingContext, points: I) -> Path
where
I: IntoIterator,
I::Item: Into<Vec2>,
{
self.points_inner(ctxt, true, points)
}
pub fn vertices<I, P, C, U>(self, ctxt: DrawingContext, points: I) -> Path
where
I: IntoIterator<Item = (P, C, U)>,
P: Into<Vec2>,
C: Into<Color>,
U: Into<Vec2>,
{
self.points_vertex_inner(ctxt, false, points)
}
pub fn vertices_closed<I, P, C, U>(self, ctxt: DrawingContext, points: I) -> Path
where
I: IntoIterator<Item = (P, C, U)>,
P: Into<Vec2>,
C: Into<Color>,
U: Into<Vec2>,
{
self.points_vertex_inner(ctxt, true, points)
}
fn points_inner<I>(self, ctxt: DrawingContext, close: bool, points: I) -> Path
where
I: IntoIterator,
I::Item: Into<Vec2>,
{
let iter = points
.into_iter()
.map(Into::into)
.map(|p| lyon::math::point(p.x, p.y));
let events = lyon::path::iterator::FromPolyline::new(close, iter);
self.events(ctxt, events)
}
fn points_vertex_inner<I, P, C, U>(self, ctxt: DrawingContext, close: bool, points: I) -> Path
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();
let path_event_src = PathEventSource::Vertex {
range: start..end,
close,
};
Path::new(
self.position,
self.orientation,
self.color,
path_event_src,
self.opts.into_options(),
)
}
}
pub(crate) fn render_path_events<I>(
events: I,
color: Option<Color>,
transform: Mat4,
options: Options,
theme: &draw::Theme,
theme_prim: &draw::theme::Primitive,
fill_tessellator: &mut lyon::tessellation::FillTessellator,
stroke_tessellator: &mut lyon::tessellation::StrokeTessellator,
mesh: &mut Mesh,
) where
I: IntoIterator<Item = lyon::path::PathEvent>,
{
let res = match options {
Options::Fill(options) => {
let color = color.unwrap_or_else(|| theme.fill(theme_prim));
let mut mesh_builder = draw::mesh::MeshBuilder::single_color(mesh, transform, color);
fill_tessellator.tessellate(events, &options, &mut mesh_builder)
}
Options::Stroke(options) => {
let color = color.unwrap_or_else(|| theme.stroke(theme_prim));
let mut mesh_builder = draw::mesh::MeshBuilder::single_color(mesh, transform, color);
stroke_tessellator.tessellate(events, &options, &mut mesh_builder)
}
};
if let Err(err) = res {
eprintln!("failed to tessellate path: {:?}", err);
}
}
pub(crate) fn render_path_points_themed<I>(
points_themed: I,
close: bool,
color: Option<Color>,
transform: Mat4,
options: Options,
theme: &draw::Theme,
theme_prim: &draw::theme::Primitive,
fill_tessellator: &mut lyon::tessellation::FillTessellator,
stroke_tessellator: &mut lyon::tessellation::StrokeTessellator,
mesh: &mut Mesh,
) where
I: IntoIterator<Item = (Vec2, Vec2)>,
{
let path = match points_themed_to_lyon_path(points_themed, close) {
None => return,
Some(p) => p,
};
let res = match options {
Options::Fill(options) => {
let color = color.unwrap_or_else(|| theme.fill(theme_prim));
let mut mesh_builder = draw::mesh::MeshBuilder::single_color(mesh, transform, color);
fill_tessellator.tessellate_with_ids(
path.id_iter(),
&path,
Some(&path),
&options,
&mut mesh_builder,
)
}
Options::Stroke(options) => {
let color = color.unwrap_or_else(|| theme.fill(theme_prim));
let mut mesh_builder = draw::mesh::MeshBuilder::single_color(mesh, transform, color);
stroke_tessellator.tessellate_with_ids(
path.id_iter(),
&path,
Some(&path),
&options,
&mut mesh_builder,
)
}
};
if let Err(err) = res {
eprintln!("failed to tessellate path: {:?}", err);
}
}
pub(crate) fn render_path_vertex<I>(
points_vertex: I,
close: bool,
transform: Mat4,
options: Options,
fill_tessellator: &mut FillTessellator,
stroke_tessellator: &mut StrokeTessellator,
mesh: &mut Mesh,
) where
I: IntoIterator<Item = (Vec2, Color, Vec2)>,
{
let path = match points_vertex_to_lyon_path(points_vertex, close) {
None => return,
Some(p) => p,
};
let mut mesh_builder = draw::mesh::MeshBuilder::vertex_per_point(mesh, transform);
let res = match options {
Options::Fill(options) => fill_tessellator.tessellate_with_ids(
path.id_iter(),
&path,
Some(&path),
&options,
&mut mesh_builder,
),
Options::Stroke(options) => stroke_tessellator.tessellate_with_ids(
path.id_iter(),
&path,
Some(&path),
&options,
&mut mesh_builder,
),
};
if let Err(err) = res {
eprintln!("failed to tessellate path: {:?}", err);
}
}
pub(crate) fn render_path_source(
path_src: PathEventSourceIter,
color: Option<Color>,
transform: Mat4,
options: Options,
theme: &draw::Theme,
theme_prim: &draw::theme::Primitive,
fill_tessellator: &mut FillTessellator,
stroke_tessellator: &mut StrokeTessellator,
mesh: &mut Mesh,
) {
match path_src {
PathEventSourceIter::Events(events) => render_path_events(
events,
color,
transform,
options,
theme,
theme_prim,
fill_tessellator,
stroke_tessellator,
mesh,
),
PathEventSourceIter::Vertex { points, close } => render_path_vertex(
points,
close,
transform,
options,
fill_tessellator,
stroke_tessellator,
mesh,
),
}
}
impl draw::render::RenderPrimitive for Path {
fn render_primitive(self, mut ctxt: draw::render::RenderContext, mesh: &mut Mesh) {
let Path {
color,
position,
orientation,
path_event_src,
options,
..
} = self;
let global_transform = *ctxt.transform;
let local_transform = position.transform() * orientation.transform();
let transform = global_transform * local_transform;
let render =
|src: PathEventSourceIter,
theme: &draw::Theme,
fill_tessellator: &mut lyon::tessellation::FillTessellator,
stroke_tessellator: &mut lyon::tessellation::StrokeTessellator| {
render_path_source(
src,
color,
transform,
options,
theme,
&draw::theme::Primitive::Path,
fill_tessellator,
stroke_tessellator,
mesh,
)
};
match path_event_src {
PathEventSource::Buffered(range) => {
let mut events = ctxt.path_event_buffer[range].iter().cloned();
let src = PathEventSourceIter::Events(&mut events);
render(
src,
&ctxt.theme,
&mut ctxt.fill_tessellator,
&mut ctxt.stroke_tessellator,
);
}
PathEventSource::Vertex { range, close } => {
let mut points_colored = ctxt.path_points_vertex_buffer[range].iter().cloned();
let src = PathEventSourceIter::Vertex {
points: &mut points_colored,
close,
};
render(
src,
&ctxt.theme,
&mut ctxt.fill_tessellator,
&mut ctxt.stroke_tessellator,
);
}
}
}
}
pub fn points_themed_to_lyon_path<I>(points: I, close: bool) -> Option<lyon::path::Path>
where
I: IntoIterator<Item = (Vec2, Vec2)>,
{
let channels = 2;
let mut path_builder = lyon::path::Path::builder_with_attributes(channels);
let mut iter = points.into_iter();
let (first_point, first_tex_coord) = iter.next()?;
let p = first_point.to_array().into();
let [u, v] = first_tex_coord.to_array();
path_builder.begin(p, &[u, v]);
for (point, text_coord) in iter {
let p = point.to_array().into();
let [u, v] = text_coord.to_array();
path_builder.line_to(p, &[u, v]);
}
path_builder.end(close);
Some(path_builder.build())
}
pub fn points_vertex_to_lyon_path<I>(points: I, close: bool) -> Option<lyon::path::Path>
where
I: IntoIterator<Item = (Vec2, Color, Vec2)>,
{
let channels = 6;
let mut path_builder = lyon::path::Path::builder_with_attributes(channels);
let mut iter = points.into_iter();
let (first_point, first_color, first_tex_coord) = iter.next()?;
let p = first_point.to_array().into();
let [r, g, b, a] = first_color.to_linear().to_f32_array();
let [u, v] = first_tex_coord.to_array();
path_builder.begin(p, &[r, g, b, a, u, v]);
for (point, color, text_coord) in iter {
let p = point.to_array().into();
let [r, g, b, a] = color.to_linear().to_f32_array();
let [u, v] = text_coord.to_array();
path_builder.line_to(p, &[r, g, b, a, u, v]);
}
path_builder.end(close);
Some(path_builder.build())
}
impl Path {
fn new(
position: position::Properties,
orientation: orientation::Properties,
color: Option<Color>,
path_event_src: PathEventSource,
options: Options,
) -> Self {
Path {
color,
orientation,
position,
path_event_src,
options,
}
}
}
impl<'a> DrawingPathInit<'a> {
pub fn fill(self) -> DrawingPathFill<'a> {
path_fill(&self.draw, self.index);
self.transition()
}
pub fn stroke(self) -> DrawingPathStroke<'a> {
path_stroke(&self.draw, self.index);
self.transition()
}
}
impl<'a> DrawingPathFill<'a> {
pub fn tolerance(self, tolerance: f32) -> Self {
self.fill_tolerance(tolerance)
}
pub fn rule(self, rule: lyon::tessellation::FillRule) -> Self {
self.fill_rule(rule)
}
}
impl<'a> DrawingPathStroke<'a> {
pub fn weight(self, weight: f32) -> Self {
self.stroke_weight(weight)
}
pub fn tolerance(self, tolerance: f32) -> Self {
self.stroke_tolerance(tolerance)
}
}
impl<'a, T> DrawingPathOptions<'a, T>
where
T: TessellationOptions,
{
pub fn events<I>(self, events: I) -> DrawingPath<'a>
where
I: IntoIterator<Item = lyon::path::PathEvent>,
{
let mut events = events.into_iter();
path_events(&self.draw, self.index, &mut events);
self.transition()
}
pub fn points<I>(self, points: I) -> DrawingPath<'a>
where
I: IntoIterator,
I::Item: Into<Vec2>,
{
let mut points = points.into_iter().map(Into::into);
path_points(&self.draw, self.index, false, &mut points);
self.transition()
}
pub fn points_closed<I>(self, points: I) -> DrawingPath<'a>
where
I: IntoIterator,
I::Item: Into<Vec2>,
{
let mut points = points.into_iter().map(Into::into);
path_points(&self.draw, self.index, true, &mut points);
self.transition()
}
pub fn points_colored<I, P, C>(self, points: I) -> DrawingPath<'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));
path_points_vertex(&self.draw, self.index, false, &mut points);
self.transition()
}
pub fn points_colored_closed<I, P, C>(self, points: I) -> DrawingPath<'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));
path_points_vertex(&self.draw, self.index, true, &mut points);
self.transition()
}
pub fn points_vertex<I, P, C, U>(self, points: I) -> DrawingPath<'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()));
path_points_vertex(&self.draw, self.index, false, &mut points);
self.transition()
}
pub fn points_vertex_closed<I, P, C, U>(self, points: I) -> DrawingPath<'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()));
path_points_vertex(&self.draw, self.index, true, &mut points);
self.transition()
}
}
fn path_fill(draw: &draw::Draw, index: usize) {
drawing::with_primitive(draw, index, |prim| match prim {
Primitive::PathInit(_) => *prim = Primitive::PathFill(PathInit.fill()),
_ => bevy::log::warn_once!("expected a `PathInit` primitive"),
})
}
fn path_stroke(draw: &draw::Draw, index: usize) {
drawing::with_primitive(draw, index, |prim| match prim {
Primitive::PathInit(_) => *prim = Primitive::PathStroke(PathInit.stroke()),
_ => bevy::log::warn_once!("expected a `PathInit` primitive"),
})
}
fn path_events(draw: &draw::Draw, index: usize, events: &mut dyn Iterator<Item = PathEvent>) {
drawing::with_primitive_ctxt(draw, index, |prim, ctxt| match prim {
Primitive::PathFill(opts) => Primitive::Path(opts.events(ctxt, events)),
Primitive::PathStroke(opts) => Primitive::Path(opts.events(ctxt, events)),
other => {
bevy::log::warn_once!("expected a `PathFill` or `PathStroke` primitive");
other
}
})
}
fn path_points(
draw: &draw::Draw,
index: usize,
close: bool,
points: &mut dyn Iterator<Item = Vec2>,
) {
drawing::with_primitive_ctxt(draw, index, |prim, ctxt| match prim {
Primitive::PathFill(opts) => Primitive::Path(opts.points_inner(ctxt, close, points)),
Primitive::PathStroke(opts) => Primitive::Path(opts.points_inner(ctxt, close, points)),
other => {
bevy::log::warn_once!("expected a `PathFill` or `PathStroke` primitive");
other
}
})
}
fn path_points_vertex(
draw: &draw::Draw,
index: usize,
close: bool,
points: &mut dyn Iterator<Item = (Vec2, Color, Vec2)>,
) {
drawing::with_primitive_ctxt(draw, index, |prim, ctxt| match prim {
Primitive::PathFill(opts) => Primitive::Path(opts.points_vertex_inner(ctxt, close, points)),
Primitive::PathStroke(opts) => {
Primitive::Path(opts.points_vertex_inner(ctxt, close, points))
}
other => {
bevy::log::warn_once!("expected a `PathFill` or `PathStroke` primitive");
other
}
})
}
impl SetFill for PathFill {
fn fill_options_mut(&mut self) -> &mut FillOptions {
&mut self.opts
}
}
impl SetStroke for PathStroke {
fn stroke_options_mut(&mut self) -> &mut StrokeOptions {
&mut self.opts
}
}
impl TessellationOptions for FillOptions {
type Tessellator = FillTessellator;
fn into_options(self) -> Options {
Options::Fill(self)
}
}
impl TessellationOptions for StrokeOptions {
type Tessellator = StrokeTessellator;
fn into_options(self) -> Options {
Options::Stroke(self)
}
}
impl<T> SetOrientation for PathOptions<T> {
fn properties(&mut self) -> &mut orientation::Properties {
SetOrientation::properties(&mut self.orientation)
}
}
impl<T> SetPosition for PathOptions<T> {
fn properties(&mut self) -> &mut position::Properties {
SetPosition::properties(&mut self.position)
}
}
impl<T> SetColor for PathOptions<T> {
fn color_mut(&mut self) -> &mut Option<Color> {
SetColor::color_mut(&mut self.color)
}
}
impl SetOrientation for Path {
fn properties(&mut self) -> &mut orientation::Properties {
SetOrientation::properties(&mut self.orientation)
}
}
impl SetPosition for Path {
fn properties(&mut self) -> &mut position::Properties {
SetPosition::properties(&mut self.position)
}
}
impl SetColor for Path {
fn color_mut(&mut self) -> &mut Option<Color> {
SetColor::color_mut(&mut self.color)
}
}
impl From<PathInit> for Primitive {
fn from(prim: PathInit) -> Self {
Primitive::PathInit(prim)
}
}
impl From<PathStroke> for Primitive {
fn from(prim: PathStroke) -> Self {
Primitive::PathStroke(prim)
}
}
impl From<PathFill> for Primitive {
fn from(prim: PathFill) -> Self {
Primitive::PathFill(prim)
}
}
impl From<Path> for Primitive {
fn from(prim: Path) -> Self {
Primitive::Path(prim)
}
}