1use eg_seven_segment::SevenSegmentStyleBuilder;
2use embedded_graphics::prelude::Transform;
3use embedded_graphics::prelude::WebColors;
4use embedded_graphics::primitives::PrimitiveStyle;
5use embedded_graphics::primitives::{Rectangle, StyledDrawable};
6
7use embedded_graphics::text::renderer::CharacterStyle;
8use embedded_graphics::{
9 Drawable,
10 geometry::AnchorX,
11 pixelcolor::Rgb666,
12 prelude::{Point, RgbColor, Size},
13 text::{Alignment, Text},
14};
15
16use super::init_running::{
17 BATT_HEIGHT, BATT_POS, BATT_WIDTH, EFF_FONT_HEIGHT, EFF_FONT_WIDTH, EFF_POS, SPEED_FONT_HEIGHT,
18 SPEED_FONT_WIDTH,
19};
20use crate::display_mod::{CENTER_POINT, DisplayDevice};
21
22fn greater_than_10(val: u32) -> bool {
23 val >= 10
24}
25
26fn render_speed_widgets(display: &mut DisplayDevice, speed: u32, prev_speed: u32) {
27 const DIGIT_SPACING: u32 = 4;
28 let speed_style = SevenSegmentStyleBuilder::new()
29 .digit_size(Size::new(SPEED_FONT_WIDTH, SPEED_FONT_HEIGHT))
30 .digit_spacing(DIGIT_SPACING)
31 .segment_width(6)
32 .segment_color(Rgb666::RED)
33 .inactive_segment_color(Rgb666::BLACK)
34 .build();
35 let mut clear_style = speed_style.clone();
36 clear_style.set_text_color(Some(Rgb666::BLACK));
37
38 const SPEED_POS: Point = Point::new(
39 CENTER_POINT.x + SPEED_FONT_WIDTH as i32,
40 CENTER_POINT.y + SPEED_FONT_HEIGHT as i32 / 2,
41 );
42 const CLEAR_TEXT_POS: Point = Point::new(
43 CENTER_POINT.x - (DIGIT_SPACING as i32),
44 CENTER_POINT.y + SPEED_FONT_HEIGHT as i32 / 2,
45 );
46
47 let mut str_buffer = itoa::Buffer::new();
48 let speed_str = str_buffer.format(speed);
49
50 if greater_than_10(prev_speed) && !greater_than_10(speed) {
52 Text::with_alignment("8", CLEAR_TEXT_POS, clear_style, Alignment::Right)
53 .draw(display)
54 .unwrap();
55 }
56 Text::with_alignment(speed_str, SPEED_POS, speed_style, Alignment::Right)
58 .draw(display)
59 .unwrap();
60}
61
62fn render_tach_widgets(display: &mut DisplayDevice, rpm: u32, _prev_rpm: u32) {
63 let tach_line_width = 3;
65
66 let tach_lines = 5;
68 let max_tach_lines = tach_lines * 5;
70
71 let tach_empty_style = PrimitiveStyle::with_fill(Rgb666::CSS_SILVER);
72
73 let tach_line_style = PrimitiveStyle::with_fill(Rgb666::RED);
74 let tach_line = Rectangle::new(
75 CENTER_POINT.x_axis() - Point::new(max_tach_lines * tach_line_width * 2, -15),
76 Size::new(tach_line_width as u32, 55),
77 );
78
79 let tach_divider_style = PrimitiveStyle::with_fill(Rgb666::CSS_DEEP_PINK);
80 let tach_divider_line = tach_line.resized_width(tach_line_width as u32 + 2, AnchorX::Left);
81
82 let tach_spacer = 4;
85 let display_rpm = ((rpm as f32 / 5000f32) * max_tach_lines as f32) as i32;
87 for i in 0..=display_rpm {
88 let (bar, bar_style) = if (i % tach_lines) == 0 {
89 (tach_divider_line, tach_divider_style)
90 } else {
91 (tach_line, tach_line_style)
92 };
93 bar.translate(Point::new(i * tach_line_width as i32 * tach_spacer, 0))
94 .draw_styled(&bar_style, display)
95 .unwrap();
96 }
97 for i in (display_rpm + 1)..=max_tach_lines {
98 let tach_line = if (i % tach_lines) == 0 {
99 tach_divider_line
100 } else {
101 tach_line
102 };
103 tach_line
104 .translate(Point::new(i * tach_line_width as i32 * tach_spacer, 0))
105 .draw_styled(&tach_empty_style, display)
106 .unwrap();
107 }
108}
109
110fn render_efficiency_gui(display: &mut DisplayDevice, efficiency: u8, prev_efficiency: u8) {
111 const DIGIT_SPACING: u32 = 2;
112 let eff_style = SevenSegmentStyleBuilder::new()
113 .digit_size(Size::new(EFF_FONT_WIDTH, EFF_FONT_HEIGHT))
114 .digit_spacing(DIGIT_SPACING)
115 .segment_width(3)
116 .segment_color(Rgb666::GREEN)
117 .inactive_segment_color(Rgb666::BLACK)
118 .build();
119 let mut clear_style = eff_style.clone();
120 clear_style.set_text_color(Some(Rgb666::BLACK));
121
122 let mut str_buffer = itoa::Buffer::new();
123 let efficiency_str = str_buffer.format(efficiency);
124
125 const EFF_TEXT_POS: Point = Point::new(
126 EFF_POS.x + EFF_FONT_WIDTH as i32,
127 EFF_POS.y + EFF_FONT_HEIGHT as i32 / 2,
128 );
129 const CLEAR_TEXT_POS: Point = Point::new(
130 EFF_POS.x - (DIGIT_SPACING as i32),
131 EFF_POS.y + EFF_FONT_HEIGHT as i32 / 2,
132 );
133
134 if prev_efficiency >= 100 && efficiency < 100 {
136 Text::with_alignment("88", CLEAR_TEXT_POS, clear_style, Alignment::Right)
137 .draw(display)
138 .unwrap();
139 } else if prev_efficiency >= 10 && efficiency < 10 {
140 Text::with_alignment("8", CLEAR_TEXT_POS, clear_style, Alignment::Right)
141 .draw(display)
142 .unwrap();
143 }
144 Text::with_alignment(efficiency_str, EFF_TEXT_POS, eff_style, Alignment::Right)
146 .draw(display)
147 .unwrap();
148}
149
150fn render_battery_gui(display: &mut DisplayDevice, battery_health: u8, prev_battery_health: u8) {
151 let mut str_buffer = itoa::Buffer::new();
152 let battery_health_str = str_buffer.format(battery_health);
153
154 let clear_style = PrimitiveStyle::with_fill(Rgb666::BLACK);
155 let fill_style = PrimitiveStyle::with_fill(Rgb666::GREEN);
156
157 const BATT_FONT_WIDTH: u32 = 10;
158 const BATT_FONT_HEIGHT: u32 = 20;
159
160 const DIGIT_SPACING: u32 = 2;
161 let batt_text_style = SevenSegmentStyleBuilder::new()
162 .digit_size(Size::new(BATT_FONT_WIDTH, BATT_FONT_HEIGHT))
163 .digit_spacing(DIGIT_SPACING)
164 .segment_width(2)
165 .segment_color(Rgb666::WHITE)
166 .inactive_segment_color(Rgb666::BLACK)
167 .build();
168 let mut clear_text_style = batt_text_style.clone();
169 clear_text_style.set_text_color(Some(Rgb666::BLACK));
170
171 const BATT_TEXT_POS: Point = Point::new(
172 BATT_POS.x - 1 * (BATT_WIDTH / 2 + BATT_FONT_WIDTH) as i32,
173 BATT_POS.y + 40,
174 );
175 const CLEAR_TEXT_POS: Point = Point::new(
176 BATT_POS.x - 1 * (BATT_WIDTH / 2 + BATT_FONT_WIDTH * 2) as i32 - DIGIT_SPACING as i32,
177 BATT_POS.y + 40,
178 );
179
180 let battery_level = ((100 - battery_health) as f32 / 100f32 * BATT_HEIGHT as f32) as u32;
181
182 let batt_outline = Rectangle::new(BATT_POS, Size::new(BATT_WIDTH, battery_level));
183 let batt_fill = Rectangle::with_corners(
184 BATT_POS + Point::new(0, battery_level as i32),
185 BATT_POS + Size::new(BATT_WIDTH - 1, BATT_HEIGHT),
186 );
187
188 batt_outline.draw_styled(&clear_style, display).unwrap();
190 batt_fill.draw_styled(&fill_style, display).unwrap();
191
192 if prev_battery_health >= 100 && battery_health < 100 {
194 Text::with_alignment("88", CLEAR_TEXT_POS, clear_text_style, Alignment::Right)
195 .draw(display)
196 .unwrap();
197 } else if prev_battery_health >= 10 && battery_health < 10 {
198 Text::with_alignment("8", CLEAR_TEXT_POS, clear_text_style, Alignment::Right)
199 .draw(display)
200 .unwrap();
201 }
202 Text::with_alignment(
204 battery_health_str,
205 BATT_TEXT_POS,
206 batt_text_style,
207 Alignment::Right,
208 )
209 .draw(display)
210 .unwrap();
211}
212
213pub fn render_running_gui(display: &mut DisplayDevice) {
214 let prev_rpm = 1500;
218 let prev_speed = 20;
219
220 let rpm = 1500;
221 let speed = 20;
222 render_tach_widgets(display, rpm as u32, prev_rpm as u32);
223 render_speed_widgets(display, speed as u32, prev_speed as u32);
224 render_efficiency_gui(display, 50, 50);
225 render_battery_gui(display, 50, 50);
226}