buffer_graphics_lib/shapes/polyline/
mod.rs1pub mod error;
2pub mod rendering;
3
4use crate::shapes::polyline::error::PolylineError;
5use crate::shapes::polyline::error::PolylineError::{InvalidPolyline, PolylineAlreadyClosed};
6use crate::shapes::polyline::Segment::*;
7use graphics_shapes::coord::Coord;
8use ici_files::prelude::*;
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
14pub enum Segment {
15 Start(Coord),
16 LineTo(Coord),
17 ArcAround {
18 center: Coord,
19 angle_start: isize,
20 angle_end: isize,
21 radius: usize,
22 },
23}
24
25impl Segment {
26 fn end_coord(&self) -> Coord {
27 match self {
28 Start(c) => *c,
29 LineTo(c) => *c,
30 ArcAround {
31 center,
32 radius,
33 angle_end,
34 ..
35 } => Coord::from_angle(center, *radius, *angle_end),
36 }
37 }
38}
39
40#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
41#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct Polyline {
43 segments: Vec<Segment>,
44 color: Color,
45 closed: bool,
46}
47
48impl Polyline {
49 pub fn new(segments: Vec<Segment>, color: Color) -> Self {
50 Self {
51 segments,
52 color,
53 closed: false,
54 }
55 }
56
57 pub fn start<P: Into<Coord>>(start_at: P, color: Color) -> Self {
58 Self {
59 segments: vec![Start(start_at.into())],
60 color,
61 closed: false,
62 }
63 }
64
65 pub fn rounded_rect(
66 left: isize,
67 top: isize,
68 right: isize,
69 bottom: isize,
70 corner_size: usize,
71 color: Color,
72 ) -> Result<Self, PolylineError> {
73 let corner_size = corner_size as isize;
74 let tl_arc = Coord::from((left + corner_size, top + corner_size));
75 let tr_arc = Coord::from((right - corner_size, top + corner_size));
76 let bl_arc = Coord::from((left + corner_size, bottom - corner_size));
77 let br_arc = Coord::from((right - corner_size, bottom - corner_size));
78
79 let line1_end = Coord::from((right - corner_size, top));
80 let line2_end = Coord::from((right, bottom - corner_size));
81 let line3_end = Coord::from((left + corner_size, bottom));
82 let line4_end = Coord::from((left, top + corner_size));
83
84 Polyline::start((left + corner_size, top), color)
85 .add_line_to(line1_end)?
86 .add_arc_around(tr_arc, corner_size as usize, 0, 90)?
87 .add_line_to(line2_end)?
88 .add_arc_around(br_arc, corner_size as usize, 90, 90)?
89 .add_line_to(line3_end)?
90 .add_arc_around(bl_arc, corner_size as usize, 180, 90)?
91 .add_line_to(line4_end)?
92 .add_arc_around(tl_arc, corner_size as usize, 270, 90)
93 }
94}
95
96impl Polyline {
97 pub fn with_color(&self, color: Color) -> Self {
98 let mut cloned = self.clone();
99 cloned.color = color;
100 cloned
101 }
102}
103
104impl Polyline {
105 pub fn add_line_to<P: Into<Coord>>(mut self, point: P) -> Result<Self, PolylineError> {
106 if self.closed {
107 return Err(PolylineAlreadyClosed);
108 }
109 self.segments.push(LineTo(point.into()));
110 Ok(self)
111 }
112
113 pub fn add_arc_around<P: Into<Coord>>(
114 mut self,
115 center: P,
116 radius: usize,
117 start_angle: isize,
118 sweep_angle: usize,
119 ) -> Result<Self, PolylineError> {
120 if self.closed {
121 return Err(PolylineAlreadyClosed);
122 }
123 self.segments.push(ArcAround {
124 center: center.into(),
125 radius,
126 angle_start: start_angle,
127 angle_end: start_angle + (sweep_angle as isize),
128 });
129 Ok(self)
130 }
131
132 pub fn close(self) -> Result<Self, PolylineError> {
133 if let Start(coord) = self.segments[0] {
134 let mut tmp = self.add_line_to(coord)?;
135 tmp.closed = true;
136 Ok(tmp)
137 } else {
138 Err(InvalidPolyline)
139 }
140 }
141}
142
143#[cfg(test)]
144mod test {
145 use crate::shapes::polyline::Polyline;
146 use crate::shapes::polyline::Segment::*;
147 use graphics_shapes::coord::Coord;
148 use ici_files::prelude::RED;
149
150 #[test]
151 fn check_closing() {
152 let polyline = Polyline::start((10, 10), RED)
153 .add_line_to((20, 10))
154 .unwrap()
155 .add_line_to((20, 20))
156 .unwrap()
157 .add_line_to((10, 20))
158 .unwrap()
159 .close()
160 .unwrap();
161 assert_eq!(
162 polyline.segments,
163 vec![
164 Start(Coord::new(10, 10)),
165 LineTo(Coord::new(20, 10)),
166 LineTo(Coord::new(20, 20)),
167 LineTo(Coord::new(10, 20)),
168 LineTo(Coord::new(10, 10)),
169 ]
170 )
171 }
172}