use std::{collections::HashMap, fmt::Debug};
use crate::helper::{
math::{*, non_nan_type::*},
arrays::{bin_arr_bounded, distinct_in_table_non_nan},
charset::{gradient_chars::*, NULL_STR},
axes::add_opt_axes_and_opt_titles,
mat_plot_lib::pyplot,
rendering::RenderableTextBuilder,
file::save_to_file,
};
fn choose_character_set(num_distinct: u32) -> Vec<String> {
if num_distinct <= binary_chars().len() as u32 {
return binary_chars();
} else if num_distinct <= shade_chars().len() as u32 {
return shade_chars();
} else if num_distinct <= ascii_chars().len() as u32 {
return ascii_chars();
} else {
return ascii_chars();
}
}
pub fn bin_arr(data: &Vec<Vec<f64>>, bins: u32) -> Vec<Vec<u32>> {
bin_arr_bounded(&data, bins, (
min_always(&(data.iter().map(
|i| min_always(i, 0.)
).collect::<Vec<f64>>()), 0.),
max_always(&(data.iter().map(
|i| max_always(i, 0.)
).collect::<Vec<f64>>()), 0.)))
}
#[derive(Clone)]
pub struct ArrayPlotBuilder<'a, T: PartialOrd + Copy> {
data: &'a Vec<Vec<T>>,
title: Option<&'a str>,
axes: Option<bool>,
chars: Option<Vec<String>>,
}
struct ArrayPlot<'a, T: PartialOrd + Copy> {
data: &'a Vec<Vec<T>>,
title: Option<&'a str>,
axes: bool,
chars: Vec<String>,
}
impl<'a, T: PartialOrd + Copy + Debug> ArrayPlotBuilder<'a, T> {
fn from(data: &Vec<Vec<T>>) -> ArrayPlotBuilder<T> {
ArrayPlotBuilder {
data: data,
title: None,
axes: None,
chars: None,
}
}
pub fn set_title<'b: 'a>(&mut self, title: &'b str) -> &mut Self {
self.title = Some(title);
self
}
pub fn set_axes(&mut self, do_axes: bool) -> &mut Self {
self.axes = Some(do_axes);
self
}
pub fn set_chars(&mut self, chars: Vec<String>) -> &mut Self {
self.chars = Some(chars);
self
}
fn build(&self) -> ArrayPlot<T> {
ArrayPlot {
data: self.data,
title: self.title,
axes: self.axes.unwrap_or(true),
chars: self.chars.clone().unwrap_or_else(|| choose_character_set(distinct_in_table_non_nan(&self.data).len() as u32)),
}
}
pub fn as_string(&self) -> String {
self.build().as_string()
}
pub fn print(&self) {
self.build().print();
}
pub fn save(&self, path: &str) {
save_to_file(&self.build().as_string(), path);
}
pub fn as_image(&self) -> RenderableTextBuilder {
RenderableTextBuilder::from(self.build().as_string())
}
pub fn pyplot(&self) {
self.build().pyplot(None);
}
pub fn save_pyplot(&self, path: &str) {
self.build().pyplot(Some(path));
}
#[allow(dead_code)]
pub(crate) fn plot(&self) -> String {
self.build().plot()
}
}
impl<'a, T: PartialOrd + Copy + Debug> ArrayPlot<'a, T> {
fn plot(&self) -> String {
let mut di = distinct_in_table_non_nan(self.data);
di.sort_unstable();
let ref_chars: Vec<&str> = subdivide_round(0, self.chars.len() as i32 - 1, di.len() as u32)
.into_iter()
.map(|i| self.chars[i as usize].as_str())
.collect::<Vec<&str>>();
let charmap: HashMap<NonNanWrapper<T>, &str> = di.into_iter().zip(ref_chars.into_iter()).collect();
self.data.iter().map(|i| {
i.iter().map(|j| {
if j == j {
charmap.get(&NonNanWrapper::from(*j)).unwrap()
} else {NULL_STR} }).collect::<String>()
}).collect::<Vec<String>>()
.join("\n")
}
fn as_string(&self) -> String {
add_opt_axes_and_opt_titles(&self.plot(), ((0., self.data[0].len() as f64), (0., self.data.len() as f64)), self.axes, self.title)
}
fn print(&self) {
println!("{}", self.as_string());
}
fn pyplot(&self, path: Option<&str>) {
let command = format!("imshow({:?})", self.data);
pyplot(&command, self.title, Some(self.axes), None, path);
}
}
pub fn array_plot<T: PartialOrd + Copy + Debug>(data: &Vec<Vec<T>>) -> ArrayPlotBuilder<T> {
ArrayPlotBuilder::from(&data)
}