use super::{
render_centered_title, svg_footer, svg_header, svg_line, svg_rect_filled, svg_rect_stroked,
ChartColor, ChartLayout,
};
pub struct QualityBoxPlotData {
pub means: Vec<f64>,
pub medians: Vec<f64>,
pub lower_quartile: Vec<f64>,
pub upper_quartile: Vec<f64>,
pub lowest: Vec<f64>,
pub highest: Vec<f64>,
pub min_y: f64,
pub max_y: f64,
pub y_interval: f64,
pub x_labels: Vec<String>,
pub title: String,
}
const GOOD: ChartColor = ChartColor::new(195, 230, 195);
const BAD: ChartColor = ChartColor::new(230, 220, 195);
const UGLY: ChartColor = ChartColor::new(230, 195, 195);
const GOOD_DARK: ChartColor = ChartColor::new(175, 230, 175);
const BAD_DARK: ChartColor = ChartColor::new(230, 215, 175);
const UGLY_DARK: ChartColor = ChartColor::new(230, 175, 175);
const BOX_FILL: ChartColor = ChartColor::new(240, 240, 0);
const MEDIAN_COLOR: ChartColor = ChartColor::new(200, 0, 0);
const MEAN_COLOR: ChartColor = ChartColor::new(0, 0, 200);
pub fn render_quality_boxplot(params: &QualityBoxPlotData) -> String {
let layout = ChartLayout::new(params.min_y, params.max_y, params.y_interval);
let num_positions = params.means.len();
let base_width = layout.base_width(num_positions);
let mut svg = svg_header(layout.width, layout.height);
layout.render_background(&mut svg);
layout.render_y_labels(&mut svg);
render_centered_title(&mut svg, ¶ms.title, layout.x_offset, layout.width);
let black = ChartColor::new(0, 0, 0);
let mut last_x_label_end: f64 = 0.0;
for i in 0..num_positions {
let x = layout.x_offset + base_width * i as f64;
let (ugly, bad, good) = if i % 2 != 0 {
(&UGLY, &BAD, &GOOD)
} else {
(&UGLY_DARK, &BAD_DARK, &GOOD_DARK)
};
let ugly_top = layout.get_y(20.0);
let ugly_bottom = layout.get_y(layout.y_start);
if ugly_bottom > ugly_top {
svg.push_str(&svg_rect_filled(
x,
ugly_top,
base_width,
ugly_bottom - ugly_top,
ugly,
));
}
let bad_top = layout.get_y(28.0);
let bad_bottom = layout.get_y(20.0);
if bad_bottom > bad_top {
svg.push_str(&svg_rect_filled(
x,
bad_top,
base_width,
bad_bottom - bad_top,
bad,
));
}
let good_top = layout.get_y(params.max_y);
let good_bottom = layout.get_y(28.0);
if good_bottom > good_top {
svg.push_str(&svg_rect_filled(
x,
good_top,
base_width,
good_bottom - good_top,
good,
));
}
if i < params.x_labels.len() {
last_x_label_end = layout.render_x_category_label_at(
&mut svg,
¶ms.x_labels[i],
i,
base_width,
last_x_label_end,
);
}
}
layout.render_axes(&mut svg);
layout.render_x_axis_label(&mut svg, "Position in read (bp)");
for i in 0..num_positions {
let box_x = layout.x_offset + base_width * i as f64;
let box_top_y = layout.get_y(params.upper_quartile[i]);
let box_bottom_y = layout.get_y(params.lower_quartile[i]);
let upper_whisker_y = layout.get_y(params.highest[i]);
let lower_whisker_y = layout.get_y(params.lowest[i]);
let median_y = layout.get_y(params.medians[i]);
let center_x = box_x + base_width / 2.0;
let box_inset = 2.0;
let box_w = base_width - 4.0;
let box_h = box_bottom_y - box_top_y;
svg.push_str(&svg_rect_filled(
box_x + box_inset,
box_top_y,
box_w,
box_h,
&BOX_FILL,
));
svg.push_str(&svg_rect_stroked(
box_x + box_inset,
box_top_y,
box_w,
box_h,
&black,
));
svg.push_str(&svg_line(
center_x,
upper_whisker_y,
center_x,
box_top_y,
&black,
1.0,
));
svg.push_str(&svg_line(
box_x + box_inset,
upper_whisker_y,
box_x + base_width - box_inset,
upper_whisker_y,
&black,
1.0,
));
svg.push_str(&svg_line(
center_x,
lower_whisker_y,
center_x,
box_bottom_y,
&black,
1.0,
));
svg.push_str(&svg_line(
box_x + box_inset,
lower_whisker_y,
box_x + base_width - box_inset,
lower_whisker_y,
&black,
1.0,
));
svg.push_str(&svg_line(
box_x + box_inset,
median_y,
box_x + base_width - box_inset,
median_y,
&MEDIAN_COLOR,
1.0,
));
}
let half_bw = layout.half_base_width(num_positions);
if num_positions >= 2 {
let mut prev_x = 0i32;
let mut prev_y = 0i32;
for i in 0..num_positions {
let x = (half_bw + layout.x_offset + (base_width * i as f64)) as i32;
let y = layout.get_y(params.means[i]) as i32;
if i > 0 {
svg.push_str(&format!(
"<line x1=\"{}\" y1=\"{}\" x2=\"{}\" y2=\"{}\" stroke=\"{}\" stroke-width=\"1\"/>\n",
prev_x, prev_y, x, y, MEAN_COLOR.to_rgb_string()
));
}
prev_x = x;
prev_y = y;
}
}
svg.push_str(svg_footer());
svg
}