0
0
Fork 0

spat: Add screen

This commit is contained in:
Jannik Beyerstedt 2026-06-15 23:08:53 +02:00
commit b2ec98da2e
7 changed files with 463 additions and 30 deletions

186
src/screen.rs Normal file
View file

@ -0,0 +1,186 @@
//! Screen Output
use alloc::vec;
use embedded_graphics::prelude::*;
use embedded_graphics::{geometry, mono_font, pixelcolor, primitives, text};
use crate::applogic;
pub const WIDTH: u16 = 320;
pub const HEIGHT: u16 = 172;
#[allow(unused)]
pub fn clear<D>(target: &mut D) -> Result<(), D::Error>
where
D: DrawTarget<Color = pixelcolor::Rgb565>,
{
let style = primitives::PrimitiveStyleBuilder::new()
.fill_color(pixelcolor::Rgb565::BLACK)
.build();
primitives::Rectangle::new(
geometry::Point::new(0, 0),
geometry::Size::new(WIDTH.into(), HEIGHT.into()),
)
.into_styled(style)
.draw(target)?;
Ok(())
}
#[allow(unused)]
pub fn test_img<C, D>(target: &mut D) -> Result<(), D::Error>
where
D: DrawTarget<Color = C>,
C: RgbColor,
{
mipidsi::TestImage::new().draw(target)?;
Ok(())
}
#[allow(unused)]
pub fn draw_glosa<D>(target: &mut D, data: &[applogic::SignalGroupData]) -> Result<(), D::Error>
where
D: DrawTarget<Color = pixelcolor::Rgb565>,
{
let num_signals = data.len();
if num_signals == 0 {
return clear(target);
}
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
let sig_width = (WIDTH as i32) / (num_signals as i32);
let text_style =
mono_font::MonoTextStyle::new(&profont::PROFONT_24_POINT, pixelcolor::Rgb565::WHITE);
let blank_style = primitives::PrimitiveStyleBuilder::new()
.fill_color(pixelcolor::Rgb565::CSS_GRAY)
.build();
let rd_style = primitives::PrimitiveStyleBuilder::new()
.fill_color(pixelcolor::Rgb565::CSS_RED)
.build();
let ye_style = primitives::PrimitiveStyleBuilder::new()
.fill_color(pixelcolor::Rgb565::CSS_YELLOW) // TODO: select nicer color
.build();
let gn_style = primitives::PrimitiveStyleBuilder::new()
.fill_color(pixelcolor::Rgb565::CSS_GREEN)
.build();
// clear maneuver and timing area
let style = primitives::PrimitiveStyleBuilder::new()
.fill_color(pixelcolor::Rgb565::BLACK)
.build();
primitives::Rectangle::new(
geometry::Point::new(0, 105),
geometry::Size::new(WIDTH.into(), 67),
)
.into_styled(style)
.draw(target)?;
for (idx, sig) in data.iter().enumerate() {
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
let offset = (idx as i32) * sig_width;
let circle_dia = 50;
let circle_margin = (sig_width - circle_dia) / 2;
let centerline = offset + (sig_width / 2);
// draw phase color
let style = match sig.phase {
applogic::SignalPhase::Unknown => blank_style,
applogic::SignalPhase::Red => rd_style,
applogic::SignalPhase::Green => gn_style,
applogic::SignalPhase::RedYellow | applogic::SignalPhase::Yellow => ye_style,
};
primitives::Circle::new(
geometry::Point::new(offset + circle_margin, 10),
circle_dia as u32,
)
.into_styled(style)
.draw(target)?;
// TODO: draw lane type?
// draw maneuver
text::Text::with_alignment(
&alloc::format!("{}", maneuver_to_str(&sig.maneuver)),
geometry::Point::new(centerline, 130),
text_style,
text::Alignment::Center,
)
.draw(target)?;
// draw timing indicator
text::Text::with_alignment(
&alloc::format!("{}s", sig.min_end_sec),
geometry::Point::new(centerline, 160),
text_style,
text::Alignment::Center,
)
.draw(target)?;
}
Ok(())
}
#[allow(unused)]
pub fn draw_slash_screen<D>(target: &mut D) -> Result<(), D::Error>
where
D: DrawTarget<Color = pixelcolor::Rgb565>,
{
let text_style =
mono_font::MonoTextStyle::new(&profont::PROFONT_18_POINT, pixelcolor::Rgb565::WHITE);
let blank_style = primitives::PrimitiveStyleBuilder::new()
.fill_color(pixelcolor::Rgb565::CSS_GRAY)
.build();
let rd_style = primitives::PrimitiveStyleBuilder::new()
.fill_color(pixelcolor::Rgb565::CSS_RED)
.build();
let ye_style = primitives::PrimitiveStyleBuilder::new()
.fill_color(pixelcolor::Rgb565::CSS_YELLOW) // TODO: select nicer color
.build();
let gn_style = primitives::PrimitiveStyleBuilder::new()
.fill_color(pixelcolor::Rgb565::CSS_GREEN)
.build();
clear(target)?;
let sig_width = (WIDTH as i32) / 3;
let circle_dia = 50;
let circle_margin = (sig_width - circle_dia) / 2;
for (idx, style) in vec![rd_style, ye_style, gn_style].iter().enumerate() {
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
let offset = (idx as i32) * sig_width;
primitives::Circle::new(
geometry::Point::new(offset + circle_margin, 10),
circle_dia as u32,
)
.into_styled(*style)
.draw(target)?;
}
// draw welcome text
let centerline = (WIDTH as i32) / 2;
text::Text::with_alignment(
"C-ITS Signal Phase Display\nWaiting for GNSS fix...",
geometry::Point::new(centerline, 130),
text_style,
text::Alignment::Center,
)
.draw(target)?;
Ok(())
}
fn maneuver_to_str(value: &applogic::v2x::map::Maneuvers) -> alloc::string::String {
alloc::format!(
"{}{}{}",
if value.left_allowed { "<" } else { " " },
if value.staight_allowed { "^" } else { " " },
if value.right_allowed { ">" } else { " " },
)
}