pub mod error;
pub mod rendering;
use crate::color::Color;
use crate::shapes::polyline::error::PolylineError;
use crate::shapes::polyline::error::PolylineError::{InvalidPolyline, PolylineAlreadyClosed};
use crate::shapes::polyline::Segment::*;
use graphics_shapes::coord::Coord;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Segment {
Start(Coord),
LineTo(Coord),
ArcAround {
center: Coord,
angle_start: isize,
angle_end: isize,
radius: usize,
},
}
impl Segment {
fn end_coord(&self) -> Coord {
match self {
Start(c) => *c,
LineTo(c) => *c,
ArcAround {
center,
radius,
angle_end,
..
} => Coord::from_angle(center, *radius, *angle_end),
}
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Polyline {
segments: Vec<Segment>,
color: Color,
closed: bool,
}
impl Polyline {
pub fn new(segments: Vec<Segment>, color: Color) -> Self {
Self {
segments,
color,
closed: false,
}
}
pub fn start<P: Into<Coord>>(start_at: P, color: Color) -> Self {
Self {
segments: vec![Start(start_at.into())],
color,
closed: false,
}
}
pub fn rounded_rect(
left: isize,
top: isize,
right: isize,
bottom: isize,
corner_size: usize,
color: Color,
) -> Result<Self, PolylineError> {
let corner_size = corner_size as isize;
let tl_arc = Coord::from((left + corner_size, top + corner_size));
let tr_arc = Coord::from((right - corner_size, top + corner_size));
let bl_arc = Coord::from((left + corner_size, bottom - corner_size));
let br_arc = Coord::from((right - corner_size, bottom - corner_size));
let line1_end = Coord::from((right - corner_size, top));
let line2_end = Coord::from((right, bottom - corner_size));
let line3_end = Coord::from((left + corner_size, bottom));
let line4_end = Coord::from((left, top + corner_size));
Polyline::start((left + corner_size, top), color)
.add_line_to(line1_end)?
.add_arc_around(tr_arc, corner_size as usize, 0, 90)?
.add_line_to(line2_end)?
.add_arc_around(br_arc, corner_size as usize, 90, 90)?
.add_line_to(line3_end)?
.add_arc_around(bl_arc, corner_size as usize, 180, 90)?
.add_line_to(line4_end)?
.add_arc_around(tl_arc, corner_size as usize, 270, 90)
}
}
impl Polyline {
pub fn with_color(&self, color: Color) -> Self {
let mut cloned = self.clone();
cloned.color = color;
cloned
}
}
impl Polyline {
pub fn add_line_to<P: Into<Coord>>(mut self, point: P) -> Result<Self, PolylineError> {
if self.closed {
return Err(PolylineAlreadyClosed);
}
self.segments.push(LineTo(point.into()));
Ok(self)
}
pub fn add_arc_around<P: Into<Coord>>(
mut self,
center: P,
radius: usize,
start_angle: isize,
sweep_angle: usize,
) -> Result<Self, PolylineError> {
if self.closed {
return Err(PolylineAlreadyClosed);
}
self.segments.push(ArcAround {
center: center.into(),
radius,
angle_start: start_angle,
angle_end: start_angle + (sweep_angle as isize),
});
Ok(self)
}
pub fn close(self) -> Result<Self, PolylineError> {
if let Start(coord) = self.segments[0] {
let mut tmp = self.add_line_to(coord)?;
tmp.closed = true;
Ok(tmp)
} else {
Err(InvalidPolyline)
}
}
}
#[cfg(test)]
mod test {
use crate::color::RED;
use crate::shapes::polyline::Polyline;
use crate::shapes::polyline::Segment::*;
use graphics_shapes::coord::Coord;
#[test]
fn check_closing() {
let polyline = Polyline::start((10, 10), RED)
.add_line_to((20, 10))
.unwrap()
.add_line_to((20, 20))
.unwrap()
.add_line_to((10, 20))
.unwrap()
.close()
.unwrap();
assert_eq!(
polyline.segments,
vec![
Start(Coord::new(10, 10)),
LineTo(Coord::new(20, 10)),
LineTo(Coord::new(20, 20)),
LineTo(Coord::new(10, 20)),
LineTo(Coord::new(10, 10)),
]
)
}
}