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