1 use plotters::drawing::{
2 backend::{BackendStyle, DrawingErrorKind},
3 DrawingBackend,
4 };
5 use plotters::prelude::*;
6 use plotters::style::text_anchor::{HPos, VPos};
7 use plotters::style::RGBAColor;
8 use std::error::Error;
9
10 #[derive(Copy, Clone)]
11 enum PixelState {
12 Empty,
13 HLine,
14 VLine,
15 Cross,
16 Pixel,
17 Text(char),
18 Circle(bool),
19 }
20
21 impl PixelState {
to_char(self) -> char22 fn to_char(self) -> char {
23 match self {
24 Self::Empty => ' ',
25 Self::HLine => '-',
26 Self::VLine => '|',
27 Self::Cross => '+',
28 Self::Pixel => '.',
29 Self::Text(c) => c,
30 Self::Circle(filled) => {
31 if filled {
32 '@'
33 } else {
34 'O'
35 }
36 }
37 }
38 }
39
update(&mut self, new_state: PixelState)40 fn update(&mut self, new_state: PixelState) {
41 let next_state = match (*self, new_state) {
42 (Self::HLine, Self::VLine) => Self::Cross,
43 (Self::VLine, Self::HLine) => Self::Cross,
44 (_, Self::Circle(what)) => Self::Circle(what),
45 (Self::Circle(what), _) => Self::Circle(what),
46 (_, Self::Pixel) => Self::Pixel,
47 (Self::Pixel, _) => Self::Pixel,
48 (_, new) => new,
49 };
50
51 *self = next_state;
52 }
53 }
54
55 pub struct TextDrawingBackend(Vec<PixelState>);
56
57 impl DrawingBackend for TextDrawingBackend {
58 type ErrorType = std::io::Error;
59
get_size(&self) -> (u32, u32)60 fn get_size(&self) -> (u32, u32) {
61 (100, 30)
62 }
63
ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<std::io::Error>>64 fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<std::io::Error>> {
65 Ok(())
66 }
67
present(&mut self) -> Result<(), DrawingErrorKind<std::io::Error>>68 fn present(&mut self) -> Result<(), DrawingErrorKind<std::io::Error>> {
69 for r in 0..30 {
70 let mut buf = String::new();
71 for c in 0..100 {
72 buf.push(self.0[r * 100 + c].to_char());
73 }
74 println!("{}", buf);
75 }
76
77 Ok(())
78 }
79
draw_pixel( &mut self, pos: (i32, i32), color: &RGBAColor, ) -> Result<(), DrawingErrorKind<std::io::Error>>80 fn draw_pixel(
81 &mut self,
82 pos: (i32, i32),
83 color: &RGBAColor,
84 ) -> Result<(), DrawingErrorKind<std::io::Error>> {
85 if color.alpha() > 0.3 {
86 self.0[(pos.1 * 100 + pos.0) as usize].update(PixelState::Pixel);
87 }
88 Ok(())
89 }
90
draw_line<S: BackendStyle>( &mut self, from: (i32, i32), to: (i32, i32), style: &S, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>91 fn draw_line<S: BackendStyle>(
92 &mut self,
93 from: (i32, i32),
94 to: (i32, i32),
95 style: &S,
96 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
97 if from.0 == to.0 {
98 let x = from.0;
99 let y0 = from.1.min(to.1);
100 let y1 = from.1.max(to.1);
101 for y in y0..y1 {
102 self.0[(y * 100 + x) as usize].update(PixelState::VLine);
103 }
104 return Ok(());
105 }
106
107 if from.1 == to.1 {
108 let y = from.1;
109 let x0 = from.0.min(to.0);
110 let x1 = from.0.max(to.0);
111 for x in x0..x1 {
112 self.0[(y * 100 + x) as usize].update(PixelState::HLine);
113 }
114 return Ok(());
115 }
116
117 plotters::drawing::rasterizer::draw_line(self, from, to, style)
118 }
119
estimate_text_size<'a>( &self, text: &str, _font: &FontDesc<'a>, ) -> Result<(u32, u32), DrawingErrorKind<Self::ErrorType>>120 fn estimate_text_size<'a>(
121 &self,
122 text: &str,
123 _font: &FontDesc<'a>,
124 ) -> Result<(u32, u32), DrawingErrorKind<Self::ErrorType>> {
125 Ok((text.len() as u32, 1))
126 }
127
draw_text( &mut self, text: &str, style: &TextStyle, pos: (i32, i32), ) -> Result<(), DrawingErrorKind<Self::ErrorType>>128 fn draw_text(
129 &mut self,
130 text: &str,
131 style: &TextStyle,
132 pos: (i32, i32),
133 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
134 let (width, height) = self.estimate_text_size(text, &style.font)?;
135 let (width, height) = (width as i32, height as i32);
136 let dx = match style.pos.h_pos {
137 HPos::Left => 0,
138 HPos::Right => -width,
139 HPos::Center => -width / 2,
140 };
141 let dy = match style.pos.v_pos {
142 VPos::Top => 0,
143 VPos::Center => -height / 2,
144 VPos::Bottom => -height,
145 };
146 let offset = (pos.1 + dy).max(0) * 100 + (pos.0 + dx).max(0);
147 for (idx, chr) in (offset..).zip(text.chars()) {
148 self.0[idx as usize].update(PixelState::Text(chr));
149 }
150 Ok(())
151 }
152 }
153
draw_chart<DB: DrawingBackend>( b: DrawingArea<DB, plotters::coord::Shift>, ) -> Result<(), Box<dyn Error>> where DB::ErrorType: 'static,154 fn draw_chart<DB: DrawingBackend>(
155 b: DrawingArea<DB, plotters::coord::Shift>,
156 ) -> Result<(), Box<dyn Error>>
157 where
158 DB::ErrorType: 'static,
159 {
160 let mut chart = ChartBuilder::on(&b)
161 .margin(1)
162 .caption("Sine and Cosine", ("sans-serif", (10).percent_height()))
163 .set_label_area_size(LabelAreaPosition::Left, (5i32).percent_width())
164 .set_label_area_size(LabelAreaPosition::Bottom, (10i32).percent_height())
165 .build_ranged(-std::f64::consts::PI..std::f64::consts::PI, -1.2..1.2)?;
166
167 chart
168 .configure_mesh()
169 .disable_x_mesh()
170 .disable_y_mesh()
171 .draw()?;
172
173 chart.draw_series(LineSeries::new(
174 (-314..314).map(|x| x as f64 / 100.0).map(|x| (x, x.sin())),
175 &RED,
176 ))?;
177
178 chart.draw_series(LineSeries::new(
179 (-314..314).map(|x| x as f64 / 100.0).map(|x| (x, x.cos())),
180 &RED,
181 ))?;
182
183 b.present()?;
184
185 Ok(())
186 }
187
main() -> Result<(), Box<dyn Error>>188 fn main() -> Result<(), Box<dyn Error>> {
189 draw_chart(TextDrawingBackend(vec![PixelState::Empty; 5000]).into_drawing_area())?;
190 let b = BitMapBackend::new("plotters-doc-data/console-example.png", (1024, 768))
191 .into_drawing_area();
192 b.fill(&WHITE)?;
193 draw_chart(b)?;
194 Ok(())
195 }
196