pub mod area;
pub mod bar;
pub mod dot;
pub mod line;
pub mod pie;
use crate::font::metrics::StandardFontMetrics;
use crate::font::StandardFont;
use crate::style::Color;
#[derive(Debug, Clone)]
pub enum ChartPrimitive {
Rect {
x: f64,
y: f64,
w: f64,
h: f64,
fill: Color,
},
Line {
x1: f64,
y1: f64,
x2: f64,
y2: f64,
stroke: Color,
width: f64,
},
Polyline {
points: Vec<(f64, f64)>,
stroke: Color,
width: f64,
},
FilledPath {
points: Vec<(f64, f64)>,
fill: Color,
opacity: f64,
},
Circle {
cx: f64,
cy: f64,
r: f64,
fill: Color,
},
ArcSector {
cx: f64,
cy: f64,
r: f64,
start_angle: f64,
end_angle: f64,
fill: Color,
},
Label {
text: String,
x: f64,
y: f64,
font_size: f64,
color: Color,
anchor: TextAnchor,
},
}
#[derive(Debug, Clone, Copy)]
pub enum TextAnchor {
Left,
Center,
Right,
}
pub const DEFAULT_COLORS: &[&str] = &[
"#1a365d", "#2b6cb0", "#3182ce", "#4299e1", "#63b3ed", "#90cdf4", "#e53e3e", "#dd6b20",
"#38a169", "#805ad5",
];
pub const Y_AXIS_WIDTH: f64 = 28.0;
pub const X_AXIS_HEIGHT: f64 = 20.0;
pub const AXIS_LABEL_FONT: f64 = 8.0;
pub const LABEL_MARGIN: f64 = 4.0;
pub const TITLE_FONT: f64 = 11.0;
pub const TITLE_HEIGHT: f64 = 20.0;
pub const GRID_COLOR: Color = Color {
r: 0.88,
g: 0.88,
b: 0.88,
a: 1.0,
};
pub const AXIS_COLOR: Color = Color {
r: 0.4,
g: 0.4,
b: 0.4,
a: 1.0,
};
pub const LABEL_COLOR: Color = Color {
r: 0.3,
g: 0.3,
b: 0.3,
a: 1.0,
};
fn helvetica_metrics() -> StandardFontMetrics {
StandardFont::Helvetica.metrics()
}
pub fn measure_label(text: &str, font_size: f64) -> f64 {
helvetica_metrics().measure_string(text, font_size, 0.0)
}
pub fn nice_number(value: f64) -> f64 {
if value <= 0.0 {
return 1.0;
}
let exp = value.log10().floor();
let frac = value / 10.0_f64.powf(exp);
let nice = if frac <= 1.0 {
1.0
} else if frac <= 2.0 {
2.0
} else if frac <= 5.0 {
5.0
} else {
10.0
};
nice * 10.0_f64.powf(exp)
}
pub fn format_number(value: f64) -> String {
if value.abs() >= 1_000_000.0 {
format!("{:.1}M", value / 1_000_000.0)
} else if value.abs() >= 1_000.0 {
format!("{:.1}K", value / 1_000.0)
} else if value == value.floor() {
format!("{}", value as i64)
} else {
format!("{:.1}", value)
}
}
pub fn lighten_color(color: &Color, factor: f64) -> Color {
Color {
r: color.r + (1.0 - color.r) * factor,
g: color.g + (1.0 - color.g) * factor,
b: color.b + (1.0 - color.b) * factor,
a: color.a,
}
}
pub fn parse_hex_color(hex: &str) -> Color {
let hex = hex.trim_start_matches('#');
match hex.len() {
3 => {
let r = u8::from_str_radix(&hex[0..1].repeat(2), 16).unwrap_or(0);
let g = u8::from_str_radix(&hex[1..2].repeat(2), 16).unwrap_or(0);
let b = u8::from_str_radix(&hex[2..3].repeat(2), 16).unwrap_or(0);
Color::rgb(r as f64 / 255.0, g as f64 / 255.0, b as f64 / 255.0)
}
6 => {
let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(0);
let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(0);
let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(0);
Color::rgb(r as f64 / 255.0, g as f64 / 255.0, b as f64 / 255.0)
}
_ => Color::BLACK,
}
}
pub fn resolve_color(custom: Option<&str>, index: usize) -> Color {
match custom {
Some(c) => parse_hex_color(c),
None => parse_hex_color(DEFAULT_COLORS[index % DEFAULT_COLORS.len()]),
}
}