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