use crate::color::LinSrgba;
use crate::draw::primitive::path;
use crate::draw::primitive::Line;
use crate::draw::primitive::Primitive;
use crate::draw::properties::spatial::{orientation, position};
use crate::draw::properties::{ColorScalar, SetColor, SetOrientation, SetPosition, SetStroke};
use crate::draw::{self, Drawing};
use crate::geom::{pt2, Point2};
use crate::glam::vec2;
use lyon::tessellation::StrokeOptions;
#[derive(Clone, Debug)]
pub struct Arrow {
line: Line,
head_length: Option<f32>,
head_width: Option<f32>,
}
pub type DrawingArrow<'a> = Drawing<'a, Arrow>;
impl Arrow {
pub fn weight(self, weight: f32) -> Self {
self.map_line(|l| l.weight(weight))
}
pub fn tolerance(self, tolerance: f32) -> Self {
self.map_line(|l| l.tolerance(tolerance))
}
pub fn start(self, start: Point2) -> Self {
self.map_line(|l| l.start(start))
}
pub fn end(self, end: Point2) -> Self {
self.map_line(|l| l.end(end))
}
pub fn points(self, start: Point2, end: Point2) -> Self {
self.map_line(|l| l.points(start, end))
}
pub fn head_length(mut self, length: f32) -> Self {
self.head_length = Some(length);
self
}
pub fn head_width(mut self, width: f32) -> Self {
self.head_width = Some(width);
self
}
fn map_line<F>(self, map: F) -> Self
where
F: FnOnce(Line) -> Line,
{
let Arrow {
line,
head_length,
head_width,
} = self;
let line = map(line);
Arrow {
line,
head_length,
head_width,
}
}
}
impl<'a> DrawingArrow<'a> {
pub fn weight(self, weight: f32) -> Self {
self.map_ty(|ty| ty.weight(weight))
}
pub fn tolerance(self, tolerance: f32) -> Self {
self.map_ty(|ty| ty.tolerance(tolerance))
}
pub fn start(self, start: Point2) -> Self {
self.map_ty(|ty| ty.start(start))
}
pub fn end(self, end: Point2) -> Self {
self.map_ty(|ty| ty.end(end))
}
pub fn points(self, start: Point2, end: Point2) -> Self {
self.map_ty(|ty| ty.points(start, end))
}
pub fn head_length(self, length: f32) -> Self {
self.map_ty(|ty| ty.head_length(length))
}
pub fn head_width(self, width: f32) -> Self {
self.map_ty(|ty| ty.head_width(width))
}
}
impl SetStroke for Arrow {
fn stroke_options_mut(&mut self) -> &mut StrokeOptions {
SetStroke::stroke_options_mut(&mut self.line)
}
}
impl SetOrientation for Arrow {
fn properties(&mut self) -> &mut orientation::Properties {
SetOrientation::properties(&mut self.line)
}
}
impl SetPosition for Arrow {
fn properties(&mut self) -> &mut position::Properties {
SetPosition::properties(&mut self.line)
}
}
impl SetColor<ColorScalar> for Arrow {
fn rgba_mut(&mut self) -> &mut Option<LinSrgba> {
SetColor::rgba_mut(&mut self.line)
}
}
impl From<Arrow> for Primitive {
fn from(prim: Arrow) -> Self {
Primitive::Arrow(prim)
}
}
impl Into<Option<Arrow>> for Primitive {
fn into(self) -> Option<Arrow> {
match self {
Primitive::Arrow(prim) => Some(prim),
_ => None,
}
}
}
impl draw::renderer::RenderPrimitive for Arrow {
fn render_primitive(
self,
mut ctxt: draw::renderer::RenderContext,
mesh: &mut draw::Mesh,
) -> draw::renderer::PrimitiveRender {
let Arrow {
line,
head_length,
head_width,
} = self;
let start = line.start.unwrap_or(pt2(0.0, 0.0));
let end = line.end.unwrap_or(pt2(0.0, 0.0));
if start == end {
return draw::renderer::PrimitiveRender::default();
}
let line_w_2 = line.path.opts.line_width * 2.0;
let line_w_4 = line_w_2 * 2.0;
let head_width = head_width.unwrap_or(line_w_2);
let head_length = head_length.unwrap_or(line_w_4);
let line_dir = end - start;
let line_dir_len = line_dir.length();
let tri_len = head_length.min(line_dir_len);
let tri_dir_norm = line_dir.normalize() * tri_len;
let tri_start = end - tri_dir_norm;
let tri_end = end;
let line_start = start;
let line_end = tri_start;
let tri_a = tri_end;
let tri_w_dir = vec2(-tri_dir_norm.y, tri_dir_norm.x).normalize() * head_width;
let tri_b = tri_start + tri_w_dir;
let tri_c = tri_start - tri_w_dir;
let draw_line = line_dir_len > tri_len;
let global_transform = *ctxt.transform;
let local_transform = line.path.position.transform() * line.path.orientation.transform();
let transform = global_transform * local_transform;
let tri_points = [tri_a, tri_b, tri_c];
let tri_points = tri_points.iter().cloned().map(|p| p.to_array().into());
let close_tri = true;
let tri_events = lyon::path::iterator::FromPolyline::new(close_tri, tri_points);
path::render_path_events(
tri_events,
line.path.color,
transform,
path::Options::Fill(Default::default()),
&ctxt.theme,
&draw::theme::Primitive::Arrow,
&mut ctxt.fill_tessellator,
&mut ctxt.stroke_tessellator,
mesh,
);
if draw_line {
let line_points = [line_start, line_end];
let line_points = line_points.iter().cloned().map(|p| p.to_array().into());
let close_line = false;
let line_events = lyon::path::iterator::FromPolyline::new(close_line, line_points);
path::render_path_events(
line_events,
line.path.color,
transform,
path::Options::Stroke(line.path.opts),
&ctxt.theme,
&draw::theme::Primitive::Arrow,
&mut ctxt.fill_tessellator,
&mut ctxt.stroke_tessellator,
mesh,
);
}
draw::renderer::PrimitiveRender::default()
}
}
impl Default for Arrow {
fn default() -> Self {
let line = Default::default();
let head_length = Default::default();
let head_width = Default::default();
Arrow {
line,
head_length,
head_width,
}
}
}