pub use super::config::{Color, FileConfig, Font, RouteColor, RouteScale};
#[derive(Debug, Clone)]
pub struct PaceDistConfig {
pub font_scale: f64,
pub thickness: i32,
pub font: Font,
pub position: Option<(i32, i32)>,
pub show_pace: bool,
pub show_distance: bool,
}
impl PaceDistConfig {
pub fn new(
font_scale: f64,
thickness: i32,
font: Font,
position: Option<(i32, i32)>,
show_pace: bool,
show_distance: bool,
) -> Self {
Self {
font_scale,
thickness,
font,
position,
show_pace,
show_distance,
}
}
pub fn large_text() -> Self {
Self {
font_scale: 0.8,
thickness: 2,
font: Font::Duplex,
position: None,
show_pace: true,
show_distance: true,
}
}
pub fn pace_only() -> Self {
Self {
font_scale: 0.5,
thickness: 1,
font: Font::Simplex,
position: None,
show_pace: true,
show_distance: false,
}
}
}
impl Default for PaceDistConfig {
fn default() -> Self {
Self {
font_scale: 0.5,
thickness: 1,
font: Font::Simplex,
position: None,
show_pace: true,
show_distance: true,
}
}
}
#[derive(Debug, Clone)]
pub struct LapDataConfig {
pub position: (f64, f64),
pub font_scale: f64,
pub thickness: i32,
pub font: Font,
pub text_color: Color,
pub show_heart_rate: bool,
pub show_stride_length: bool,
pub show_pace_bars: bool,
}
impl LapDataConfig {
#[allow(clippy::too_many_arguments)]
pub fn new(
position: (f64, f64),
font_scale: f64,
thickness: i32,
font: Font,
text_color: Color,
show_heart_rate: bool,
show_stride_length: bool,
show_pace_bars: bool,
) -> Self {
Self {
position,
font_scale,
thickness,
font,
text_color,
show_heart_rate,
show_stride_length,
show_pace_bars,
}
}
pub fn minimal() -> Self {
Self {
position: (0.5, 0.09), font_scale: 0.5,
thickness: 1,
font: Font::Simplex,
text_color: Color::White,
show_heart_rate: false,
show_stride_length: false,
show_pace_bars: true,
}
}
pub fn detailed() -> Self {
Self {
position: (0.5, 0.07), font_scale: 0.5,
thickness: 1,
font: Font::Simplex,
text_color: Color::White,
show_heart_rate: true,
show_stride_length: true,
show_pace_bars: true,
}
}
}
impl Default for LapDataConfig {
fn default() -> Self {
Self {
position: (0.5, 0.09), font_scale: 0.5,
thickness: 1,
font: Font::Simplex,
text_color: Color::White,
show_heart_rate: true,
show_stride_length: true,
show_pace_bars: true,
}
}
}
#[derive(Debug, Clone)]
pub struct RouteVideoConfig {
pub route_scale: RouteScale,
pub colors: RouteColor,
pub pace_dist: PaceDistConfig,
pub lap_data: LapDataConfig,
pub file_config: FileConfig,
pub show_bottom_bar: bool,
pub show_route: bool,
pub show_lap_data: bool,
}
impl RouteVideoConfig {
#[allow(clippy::too_many_arguments)]
pub fn new(
route_scale: RouteScale,
colors: RouteColor,
pace_dist: PaceDistConfig,
lap_data: LapDataConfig,
file_config: FileConfig,
show_bottom_bar: bool,
show_route: bool,
show_lap_data: bool,
) -> Self {
Self {
route_scale,
colors,
pace_dist,
lap_data,
file_config,
show_bottom_bar,
show_route,
show_lap_data,
}
}
pub fn minimalist() -> Self {
Self {
route_scale: RouteScale::default(),
colors: RouteColor::default(),
pace_dist: PaceDistConfig::pace_only(),
lap_data: LapDataConfig::minimal(),
show_bottom_bar: true,
show_route: true,
show_lap_data: true,
file_config: FileConfig::default(),
}
}
pub fn detailed() -> Self {
Self {
route_scale: RouteScale::large(),
colors: RouteColor::default(),
pace_dist: PaceDistConfig::large_text(),
lap_data: LapDataConfig::detailed(),
show_bottom_bar: true,
show_route: true,
show_lap_data: true,
file_config: FileConfig::default(),
}
}
pub fn neon() -> Self {
Self {
route_scale: RouteScale::centered(),
colors: RouteColor::neon_scheme(),
pace_dist: PaceDistConfig::default(),
lap_data: LapDataConfig::default(),
show_bottom_bar: true,
show_route: true,
show_lap_data: true,
file_config: FileConfig::default(),
}
}
}
impl Default for RouteVideoConfig {
fn default() -> Self {
Self {
route_scale: RouteScale::default(),
colors: RouteColor::default(),
pace_dist: PaceDistConfig::default(),
lap_data: LapDataConfig::default(),
show_bottom_bar: true,
show_route: true,
show_lap_data: true,
file_config: FileConfig::default(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_color_to_bgra() {
assert_eq!(
Color::Red.to_bgra(),
[0.0, 0.0, 255.0, 0.0]
);
assert_eq!(
Color::Green.to_bgra(),
[0.0, 255.0, 0.0, 0.0]
);
assert_eq!(
Color::Blue.to_bgra(),
[255.0, 0.0, 0.0, 0.0]
);
assert_eq!(
Color::White.to_bgra(),
[255.0, 255.0, 255.0, 0.0]
);
assert_eq!(
Color::Black.to_bgra(),
[0.0, 0.0, 0.0, 0.0]
);
}
#[test]
fn test_route_scale_presets() {
let default = RouteScale::default();
assert_eq!(default.scale, 0.2);
assert_eq!(default.offset_x_percent, 0.1);
assert_eq!(default.offset_y_percent, 0.1);
let centered = RouteScale::centered();
assert_eq!(centered.scale, 0.4);
assert_eq!(centered.offset_x_percent, 0.3);
assert_eq!(centered.offset_y_percent, 0.3);
let large = RouteScale::large();
assert_eq!(large.scale, 0.7);
assert_eq!(large.offset_x_percent, 0.15);
assert_eq!(large.offset_y_percent, 0.15);
}
#[test]
fn test_route_scale_custom() {
let custom = RouteScale::new(0.5, 0.2, 0.3);
assert_eq!(custom.scale, 0.5);
assert_eq!(custom.offset_x_percent, 0.2);
assert_eq!(custom.offset_y_percent, 0.3);
}
#[test]
fn test_lap_data_position_percentages() {
let config = LapDataConfig::default();
assert!(config.position.0 >= 0.0 && config.position.0 <= 1.0);
assert!(config.position.1 >= 0.0 && config.position.1 <= 1.0);
let x = config.position.0 * 1920.0;
let y = config.position.1 * 1080.0;
assert!(x >= 0.0 && x <= 1920.0);
assert!(y >= 0.0 && y <= 1080.0);
}
#[test]
fn test_pace_dist_config() {
let default = PaceDistConfig::default();
assert!(default.show_pace);
assert!(default.show_distance);
let pace_only = PaceDistConfig::pace_only();
assert!(pace_only.show_pace);
assert!(!pace_only.show_distance);
}
#[test]
fn test_route_video_config_presets() {
let default = RouteVideoConfig::default();
assert_eq!(default.show_bottom_bar, true);
assert_eq!(default.show_route, true);
assert_eq!(default.show_lap_data, true);
assert_eq!(
default.file_config.fit_file,
"source/example.fit"
);
assert_eq!(
default.file_config.background_image,
"source/example.jpg"
);
assert_eq!(
default.file_config.output_file,
"outputs/output.mp4"
);
let minimalist = RouteVideoConfig::minimalist();
assert!(minimalist.show_bottom_bar);
let neon = RouteVideoConfig::neon();
assert!(neon.show_route);
}
#[test]
fn test_route_video_config_custom() {
let file_config = FileConfig::new(
"test.fit".to_string(),
"test.jpg".to_string(),
"test.mp4".to_string(),
);
let config = RouteVideoConfig::new(
RouteScale::new(0.5, 0.1, 0.2),
RouteColor::default(),
PaceDistConfig::default(),
LapDataConfig::default(),
file_config,
true,
false,
true,
);
assert_eq!(config.show_route, false);
assert_eq!(config.file_config.fit_file, "test.fit");
assert_eq!(
config.file_config.background_image,
"test.jpg"
);
assert_eq!(
config.file_config.output_file,
"test.mp4"
);
}
#[test]
fn test_visibility_flags() {
let mut config = RouteVideoConfig::default();
config.show_bottom_bar = false;
config.show_route = false;
config.show_lap_data = false;
assert!(!config.show_bottom_bar);
assert!(!config.show_route);
assert!(!config.show_lap_data);
}
}