1use num::ToPrimitive;
11use rayon::prelude::*;
12
13use crate::helper::{
14 arrays::{padded_vec_to, table_indices_to_counts, transpose_table},
15 axes::add_opt_axes_and_opt_titles,
16 charset::subdiv_chars::*,
17 mat_plot_lib::pyplot,
18 math::{bin_to_u8, ciel_div, max_always, min_always, pad_range},
19 file::save_to_file,
20 rendering::RenderableTextBuilder,
21};
22
23fn pad_point_range(points: &Vec<(f64, f64)>, padding: f64) -> ((f64, f64), (f64, f64)) {
25 (
26 pad_range((min_always(&points.iter().map(|i| i.0).collect(), 0.),
27 max_always(&points.iter().map(|i| i.0).collect(), 0.)), padding),
28 pad_range((min_always(&points.iter().map(|i| i.1).collect(), 0.),
29 max_always(&points.iter().map(|i| i.1).collect(), 0.)), padding)
30 )
31}
32
33pub(crate) fn padded_point_range<T: PartialOrd + Copy + ToPrimitive>(points: &Vec<(T, T)>, padding: f64) -> ((f64, f64), (f64, f64)) {
34 pad_point_range(
35 &points
36 .iter()
37 .map(|t|
38 (match t.0.to_f64() {Some(val) => val, None => 0.},
39 match t.1.to_f64() {Some(val) => val, None => 0.})
40 ).collect::<Vec<(f64, f64)>>(),
41 padding
42 )
43}
44
45pub(crate) fn determine_char_set<T: ToPrimitive + PartialEq>(points: &Vec<(T, T)>, range: ((f64, f64), (f64, f64)), size: (u32, u32)) -> (Vec<char>, (u32, u32)) {
46 let pts: Vec<&(T, T)> = points.iter().filter(|i| i.0 == i.0 && i.1 == i.1).collect();
47
48 let v: Vec<f64> = table_indices_to_counts(&points, range, size).into_iter().flatten().map(|i| i as f64).collect();
49
50 let mean_v: f64 = v.iter().sum::<f64>() / v.len() as f64;
51 let max_v: f64 = max_always(&v, 0.);
52
53 if mean_v <= 1. && max_v * ciel_div(pts.len(), 20) as f64 <= 2. {
54 (dots_one_by_one(), (1, 1))
55 } else if mean_v <= 1.5 || max_v * ciel_div(pts.len(), 10) as f64 <= 4. {
56 (blocks_two_by_two(), (2, 2))
57 } else {
58 (dots_two_by_four(), (2, 4))
59 }
60}
61
62
63pub(crate) fn bool_arr_plot_string_custom_charset(arr: &Vec<Vec<bool>>, range: (u32, u32), charset: (Vec<char>, (u32, u32))) -> String {
64 let chrs = charset.0;
67 let chrsize = charset.1;
68 let x_size = ciel_div(range.0, chrsize.0);
69 let y_size = ciel_div(range.1, chrsize.1);
70
71 assert_eq!(chrs.len() as u32, 1u32 << (chrsize.0 * chrsize.1));
73
74 (0..y_size).into_par_iter().map(|j|
75 (0..x_size).map(|i| {
76 let (x, y) = (chrsize.0 * i, chrsize.1 * j);
78 let (xn, yn) = (chrsize.0 * (i + 1), chrsize.1 * (j + 1));
79
80 chrs[
81 bin_to_u8(
83 transpose_table(
85 &padded_vec_to(
87 arr[y as usize..(yn as usize).clamp(0, arr.len())]
88 .iter()
89 .map(|row| padded_vec_to(
90 row[x as usize..(xn as usize).clamp(0, row.len())].to_vec(),
91 chrsize.0 as usize,
92 false)
93 )
94 .collect::<Vec<Vec<bool>>>(),
95
96 chrsize.1 as usize,
97 vec![false; chrsize.0 as usize],
98 )
99 )
100 .into_iter()
102 .flatten()
103 .map(|i| *i)
104 .collect::<Vec<bool>>()
105 ) as usize
106 ]
107 }).collect::<String>()
108 ).collect::<Vec<String>>()
109 .join("\n")
110}
111
112
113#[derive(Clone)]
127pub struct ScatterPlotBuilder<'a, T: PartialOrd + Copy + ToPrimitive + std::fmt::Debug> {
128 data: &'a Vec<(T, T)>,
129 domain_and_range: Option<((f64, f64), (f64, f64))>,
130 padding: Option<f64>,
131 size: Option<(u32, u32)>,
132 title: Option<&'a str>,
133 axes: Option<bool>,
134 chars: Option<(Vec<char>, (u32, u32))>,
135}
136
137struct ScatterPlot<'a, T: PartialOrd + Copy + ToPrimitive + std::fmt::Debug> {
139 data: &'a Vec<(T, T)>,
140 domain_and_range: ((f64, f64), (f64, f64)),
141 size: (u32, u32),
142 title: Option<&'a str>,
143 axes: bool,
144 chars: (Vec<char>, (u32, u32)),
145}
146
147impl<'a, T: PartialOrd + Copy + ToPrimitive + std::fmt::Debug> ScatterPlotBuilder<'a, T> {
148 fn from<'b: 'a>(data: &'b Vec<(T, T)>) -> Self {
150 ScatterPlotBuilder {
151 data: data,
152 domain_and_range: None,
153 padding: None,
154 size: None,
155 title: None,
156 axes: None,
157 chars: None,
158 }
159 }
160
161 pub fn set_range(&mut self, range: ((f64, f64), (f64, f64))) -> &mut Self {
162 self.domain_and_range = Some(range);
163 self
164 }
165
166 pub fn set_padding(&mut self, padding: f64) -> &mut Self {
167 self.padding = Some(padding);
168 self
169 }
170
171 pub fn set_size(&mut self, size: (u32, u32)) -> &mut Self {
172 self.size = Some(size);
173 self
174 }
175
176 pub fn set_title<'b: 'a>(&mut self, title: &'b str) -> &mut Self {
177 self.title = Some(title);
178 self
179 }
180
181 pub fn set_axes(&mut self, do_axes: bool) -> &mut Self {
182 self.axes = Some(do_axes);
183 self
184 }
185
186 pub fn set_chars(&mut self, chars: (Vec<char>, (u32, u32))) -> &mut Self {
191 self.chars = Some(chars);
192 self
193 }
194
195 fn build(&self) -> ScatterPlot<T> {
196 let padding = self.padding.unwrap_or(0.1);
198 let domain_and_range = self.domain_and_range.unwrap_or_else(|| padded_point_range(&self.data, padding));
199 let size = self.size.unwrap_or((60, 30));
200 let chars = self.chars.clone().unwrap_or_else(|| determine_char_set(&self.data, domain_and_range, size)); ScatterPlot {
203 data: self.data,
204 domain_and_range,
205 size: size,
206 title: self.title,
207 axes: self.axes.unwrap_or(true),
208 chars: chars,
209 }
210 }
211
212 pub fn as_string(&self) -> String {
214 self.build().as_string()
215 }
216
217 pub fn print(&self) {
219 self.build().print();
220 }
221
222 pub fn save(&self, path: &str) {
224 save_to_file(&self.build().as_string(), path);
225 }
226
227 pub fn as_image(&self) -> RenderableTextBuilder {
229 RenderableTextBuilder::from(self.build().as_string())
230 }
231
232 pub fn pyplot(&self) {
234 self.build().pyplot(None);
235 }
236
237 pub fn save_pyplot(&self, path: &str) {
239 self.build().pyplot(Some(path));
240 }
241
242 #[allow(dead_code)]
244 pub(crate) fn plot(&self) -> String {
245 self.build().plot()
246 }
247
248}
249
250impl<'a, T: PartialOrd + Copy + ToPrimitive + std::fmt::Debug> ScatterPlot<'a, T> {
251 fn plot(&self) -> String {
252 let bool_arr: Vec<Vec<bool>> = table_indices_to_counts(&self.data, self.domain_and_range, (self.size.0 * self.chars.1.0, self.size.1 * self.chars.1.1))
253 .into_par_iter()
254 .map(|i|
255 i.into_iter()
256 .map(|j| j != 0)
257 .collect()
258 ).collect();
259
260 bool_arr_plot_string_custom_charset(&bool_arr, (self.size.0 * self.chars.1.0, self.size.1 * self.chars.1.1), self.chars.clone())
261 }
262
263 fn as_string(&self) -> String {
264 add_opt_axes_and_opt_titles(&self.plot(), self.domain_and_range, self.axes, self.title)
265 }
266
267 fn print(&self) {
268 println!("{}", self.as_string());
269 }
270
271 fn pyplot(&self, path: Option<&str>) {
272 let x_data: Vec<T> = self.data.iter().map(|p| p.0).collect();
273 let y_data: Vec<T> = self.data.iter().map(|p| p.1).collect();
274 let command = format!("scatter({x_data:?}, {y_data:?})");
275
276 pyplot(&command, self.title, Some(self.axes), Some(self.domain_and_range), path);
277 }
278}
279
280pub fn scatter_plot<'a, T: PartialOrd + Copy + ToPrimitive + std::fmt::Debug>(points: &'a Vec<(T, T)>) -> ScatterPlotBuilder<'a, T> {
316 ScatterPlotBuilder::from(points)
317}
318
319pub fn list_as_points<T: ToPrimitive>(points: &Vec<T>) -> Vec<(f64, f64)> {
329 points.iter().enumerate().map(|(i, p)| (i as f64, p.to_f64().unwrap())).collect()
330}