plotters_ratatui_backend/
lib.rs1use plotters_backend::{
2 BackendColor, BackendCoord, BackendTextStyle, DrawingBackend, DrawingErrorKind,
3};
4use ratatui::style::{Color as RataColor, Style as RataStyle};
5use ratatui::widgets::canvas;
6use ratatui::{layout, text};
7
8#[cfg(feature = "widget")]
9mod widget;
10#[cfg(feature = "widget")]
11pub use widget::*;
12
13pub const CHAR_PIXEL_SIZE: u32 = 4;
14
15pub struct RatatuiBackend<'a, 'b> {
16 pub canvas: &'a mut canvas::Context<'b>,
17 pub size: layout::Rect,
18}
19
20impl<'a, 'b> DrawingBackend for RatatuiBackend<'a, 'b> {
21 type ErrorType = Error;
22
23 fn get_size(&self) -> (u32, u32) { rect_to_size(self.size) }
24
25 fn ensure_prepared(&mut self) -> Result { Ok(()) }
26
27 fn present(&mut self) -> Result { Ok(()) }
28
29 fn draw_pixel(&mut self, coord: BackendCoord, color: BackendColor) -> Result {
30 self.canvas.draw(&canvas::Points {
31 coords: &[backend_to_canvas_coords(coord, self.size)],
32 color: convert_color(color),
33 });
34 Ok(())
35 }
36
37 fn draw_text<TStyle: BackendTextStyle>(
38 &mut self,
39 text: &str,
40 style: &TStyle,
41 mut coord: BackendCoord,
42 ) -> Result {
43 let width = text.chars().count();
44 coord.0 -= (width as u32 * CHAR_PIXEL_SIZE / 2) as i32;
45
46 let (x, y) = backend_to_canvas_coords(coord, self.size);
47 self.canvas.print(x, y, text::Line::styled(text.to_string(), convert_style(style)));
48 Ok(())
49 }
50
51 fn draw_line<S: plotters_backend::BackendStyle>(
52 &mut self,
53 coord1: BackendCoord,
54 coord2: BackendCoord,
55 style: &S,
56 ) -> std::result::Result<(), DrawingErrorKind<Self::ErrorType>> {
57 let (x1, y1) = backend_to_canvas_coords(coord1, self.size);
58 let (x2, y2) = backend_to_canvas_coords(coord2, self.size);
59
60 self.canvas.draw(&canvas::Line::new(x1, y1, x2, y2, convert_color(style.color())));
61 Ok(())
62 }
63
64 fn draw_circle<S: plotters_backend::BackendStyle>(
65 &mut self,
66 coord: BackendCoord,
67 radius: u32,
68 style: &S,
69 _fill: bool,
70 ) -> std::result::Result<(), DrawingErrorKind<Self::ErrorType>> {
71 let (x, y) = backend_to_canvas_coords(coord, self.size);
72 self.canvas.draw(&canvas::Circle {
73 x,
74 y,
75 radius: radius.into(),
76 color: convert_color(style.color()),
77 });
78 Ok(())
79 }
80
81 fn draw_rect<S: plotters_backend::BackendStyle>(
82 &mut self,
83 coord1: BackendCoord,
84 coord2: BackendCoord,
85 style: &S,
86 _fill: bool,
87 ) -> std::result::Result<(), DrawingErrorKind<Self::ErrorType>> {
88 let (x1, y1) = backend_to_canvas_coords(coord1, self.size);
89 let (x2, y2) = backend_to_canvas_coords(coord2, self.size);
90
91 self.canvas.draw(&canvas::Rectangle {
92 x: x1.min(x2),
93 y: y1.min(y2),
94 width: (x2 - x1).abs(),
95 height: (y2 - y1).abs(),
96 color: convert_color(style.color()),
97 });
98 Ok(())
99 }
100
101 fn estimate_text_size<TStyle: BackendTextStyle>(
102 &self,
103 text: &str,
104 _style: &TStyle,
105 ) -> std::result::Result<(u32, u32), DrawingErrorKind<Self::ErrorType>> {
106 Ok((text.chars().count() as u32 * CHAR_PIXEL_SIZE, CHAR_PIXEL_SIZE))
107 }
108}
109
110fn rect_to_size(rect: layout::Rect) -> (u32, u32) {
111 (u32::from(rect.width) * CHAR_PIXEL_SIZE, u32::from(rect.height) * CHAR_PIXEL_SIZE)
112}
113
114fn backend_to_canvas_coords((x, y): BackendCoord, rect: layout::Rect) -> (f64, f64) {
115 let (width, height) = rect_to_size(rect);
116
117 let x = f64::from(x) / f64::from(width);
118 let mut y = f64::from(y) / f64::from(height);
119 y = 1. - y;
120 (x, y)
121}
122
123fn convert_color(color: BackendColor) -> RataColor {
124 RataColor::Rgb(color.rgb.0, color.rgb.1, color.rgb.2)
125}
126fn convert_style(style: &impl BackendTextStyle) -> RataStyle {
127 RataStyle::default().fg(convert_color(style.color()))
128}
129
130pub type Result<T = ()> = std::result::Result<T, DrawingErrorKind<Error>>;
131
132#[derive(Debug, thiserror::Error)]
133pub enum Error {}