use aksr::Builder;
use crate::ColorSource;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TextStyleMode {
Rect {
padding: f32,
},
Rounded {
padding: f32,
radius: f32,
},
}
impl Default for TextStyleMode {
fn default() -> Self {
Self::Rect { padding: 4.0 }
}
}
impl TextStyleMode {
pub fn rect(padding: f32) -> Self {
Self::Rect { padding }
}
pub fn rounded(padding: f32, radius: f32) -> Self {
Self::Rounded { padding, radius }
}
pub fn padding(&self) -> f32 {
match self {
Self::Rect { padding } | Self::Rounded { padding, .. } => *padding,
}
}
pub fn corner_radius(&self) -> f32 {
match self {
Self::Rounded { radius, .. } => *radius,
Self::Rect { .. } => 0.0,
}
}
pub fn is_rounded(&self) -> bool {
matches!(self, Self::Rounded { .. })
}
}
#[derive(Debug, Clone, Builder, PartialEq)]
pub struct TextStyle {
visible: bool,
loc: TextLoc,
mode: TextStyleMode,
font_size: Option<f32>,
draw_fill: bool,
draw_outline: bool,
thickness: usize,
color: ColorSource,
bg_fill_color: ColorSource,
bg_outline_color: ColorSource,
#[args(setter_prefix = "show")]
confidence: bool,
#[args(setter_prefix = "show")]
name: bool,
#[args(setter_prefix = "show")]
id: bool,
decimal_places: usize,
}
impl Default for TextStyle {
fn default() -> Self {
Self {
visible: true,
loc: TextLoc::OuterTopLeft,
mode: TextStyleMode::Rect { padding: 4.0 },
font_size: None, draw_fill: true,
draw_outline: false,
thickness: 0,
color: ColorSource::Auto,
bg_fill_color: ColorSource::Auto,
bg_outline_color: ColorSource::Auto,
confidence: true,
name: true,
id: true,
decimal_places: 3,
}
}
}
impl TextStyle {
pub fn should_draw(&self) -> bool {
self.visible && (self.name || self.confidence || self.id)
}
pub fn bg_color(&self) -> ColorSource {
self.bg_fill_color
}
pub fn with_bg_color(self, color: ColorSource) -> Self {
self.with_bg_fill_color(color)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum TextLoc {
InnerTopLeft,
InnerTopCenter,
InnerTopRight,
InnerBottomLeft,
InnerBottomCenter,
InnerBottomRight,
InnerCenterLeft,
InnerCenterRight,
Center,
#[default]
OuterTopLeft,
OuterTopCenter,
OuterTopRight,
OuterBottomLeft,
OuterBottomCenter,
OuterBottomRight,
OuterCenterLeft,
OuterCenterRight,
}
impl TextLoc {
pub fn compute_anchor(
&self,
bbox: (f32, f32, f32, f32),
text_size: (u32, u32),
canvas_size: (u32, u32),
padding: Option<f32>,
offset: Option<f32>,
) -> (f32, f32) {
let (xmin, ymin, xmax, ymax) = bbox;
let (text_w, text_h) = (text_size.0 as f32, text_size.1 as f32);
let (canvas_w, canvas_h) = (canvas_size.0 as f32, canvas_size.1 as f32);
let pad = padding.unwrap_or(2.0);
let off = offset.unwrap_or(0.0);
let cx = (xmin + xmax) / 2.0;
let cy = (ymin + ymax) / 2.0;
let (mut x, mut y) = match self {
TextLoc::InnerTopLeft => (xmin + pad, ymin + text_h + pad),
TextLoc::InnerTopCenter => (cx - text_w / 2.0, ymin + text_h + pad),
TextLoc::InnerTopRight => (xmax - text_w - pad, ymin + text_h + pad),
TextLoc::InnerBottomLeft => (xmin + pad, ymax - pad),
TextLoc::InnerBottomCenter => (cx - text_w / 2.0, ymax - pad),
TextLoc::InnerBottomRight => (xmax - text_w - pad, ymax - pad),
TextLoc::InnerCenterLeft => (xmin + pad, cy + text_h / 2.0),
TextLoc::InnerCenterRight => (xmax - text_w - pad, cy + text_h / 2.0),
TextLoc::Center => (cx - text_w / 2.0, cy + text_h / 2.0),
TextLoc::OuterTopLeft => (xmin, ymin - off),
TextLoc::OuterTopCenter => (cx - text_w / 2.0, ymin - off),
TextLoc::OuterTopRight => (xmax - text_w, ymin - off),
TextLoc::OuterBottomLeft => (xmin, ymax + text_h + pad + off),
TextLoc::OuterBottomCenter => (cx - text_w / 2.0, ymax + text_h + pad + off),
TextLoc::OuterBottomRight => (xmax - text_w, ymax + text_h + pad + off),
TextLoc::OuterCenterLeft => (xmin - text_w - pad, cy + text_h / 2.0),
TextLoc::OuterCenterRight => (xmax + pad, cy + text_h / 2.0),
};
if x < 0.0 {
x = 0.0;
}
if x + text_w > canvas_w {
x = (canvas_w - text_w).max(0.0);
}
if y - text_h < 0.0 {
y = text_h;
}
if y > canvas_h {
y = canvas_h;
}
(x, y)
}
}