use numfmt::Precision;
use plotters::{
backend::DrawingBackend,
chart::{ChartBuilder, ChartContext},
coord::{types::RangedCoordf64, Shift},
drawing::DrawingArea,
prelude::{Cartesian2d, WHITE},
};
use crate::{drawing_coords::DrawingCoords, plot_options::PlotOptions};
pub fn chart<'a, DB: DrawingBackend>(
root: &'a DrawingArea<DB, Shift>,
options: &PlotOptions,
drawing_coords: &DrawingCoords,
x_axis_label: &str,
y_axis_label: &str,
) -> ChartContext<'a, DB, Cartesian2d<RangedCoordf64, RangedCoordf64>> {
let mut builder = ChartBuilder::on(&root);
builder
.x_label_area_size((drawing_coords.chart_height * options.graph_label_bottom) as i32)
.margin_top(
(if options.title.is_empty() {
0.0
} else {
drawing_coords.chart_height * options.graph_title_space_top
} + (drawing_coords.chart_height * options.graph_space_top)) as i32,
)
.y_label_area_size((drawing_coords.chart_width * options.graph_label_left) as i32)
.margin_right((drawing_coords.chart_width * options.graph_space_right) as i32);
let mut chart_context = builder
.build_cartesian_2d(
(drawing_coords.x_min - drawing_coords.x_space)
..(drawing_coords.x_max + drawing_coords.x_space),
(drawing_coords.y_min - drawing_coords.y_space)
..(drawing_coords.y_max + drawing_coords.y_space),
)
.unwrap();
let mut mesh = chart_context.configure_mesh();
mesh.x_desc(x_axis_label)
.y_desc(y_axis_label)
.axis_desc_style((
options.font.as_str(),
(drawing_coords.chart_width * drawing_coords.chart_height).sqrt() * options.axis_scalar,
))
.x_label_style((
options.font.as_str(),
(drawing_coords.chart_width * drawing_coords.chart_height).sqrt()
* options.x_label_scalar,
))
.y_label_style((
options.font.as_str(),
(drawing_coords.chart_width * drawing_coords.chart_height).sqrt()
* options.y_label_scalar,
))
.axis_style(WHITE)
.bold_line_style(WHITE)
.light_line_style(WHITE)
.max_light_lines(2);
let x_steps = (drawing_coords.x_max - drawing_coords.x_min) / 11.0;
let x_log_func = |&x: &f64| {
if x.abs() < x_steps / 100.0 {
"0.0".to_string()
} else {
let number_formatter = numfmt::Formatter::new().precision(Precision::Significance(3));
let mut ret = number_formatter.clone().fmt2(10.0_f64.powf(x)).to_string();
if ret.len() > 5 {
ret = format!("{:e}", 10.0_f64.powf(x));
}
ret
}
};
let x_func = |&x: &f64| {
if x.abs() < x_steps / 100.0 {
"0.0".to_string()
} else {
let number_formatter = numfmt::Formatter::new().precision(Precision::Significance(3));
let mut ret = number_formatter.clone().fmt2(x).to_string();
if ret.len() > 5 {
ret = format!("{x:e}");
}
ret
}
};
if options.x_log {
mesh.x_label_formatter(&x_log_func);
} else {
mesh.x_label_formatter(&x_func);
}
let y_steps = (drawing_coords.y_max - drawing_coords.y_min) / 11.0;
let y_log_func = |&y: &f64| {
if y.abs() < y_steps / 100.0 {
"0.0".to_string()
} else {
let number_formatter = numfmt::Formatter::new().precision(Precision::Significance(3));
let mut ret = number_formatter.clone().fmt2(10.0_f64.powf(y)).to_string();
if ret.len() > 5 {
ret = format!("{:e}", 10.0_f64.powf(y));
}
ret
}
};
let y_func = |&y: &f64| {
if y.abs() < y_steps / 100.0 {
"0.0".to_string()
} else {
let number_formatter = numfmt::Formatter::new().precision(Precision::Significance(3));
let mut ret = number_formatter.clone().fmt2(y).to_string();
if ret.len() > 5 {
ret = format!("{y:e}");
}
ret
}
};
if options.y_log {
mesh.y_label_formatter(&y_log_func);
} else {
mesh.y_label_formatter(&y_func);
}
mesh.draw().unwrap();
chart_context
}