1use super::{generate_list_quoted, matrix_to_array, vector_to_array, AsMatrix, GraphMaker};
2use num_traits::Num;
3use std::fmt::Write;
4
5pub struct Contour {
50 colors: Vec<String>, levels: Vec<f64>, colormap_name: String, no_lines: bool, no_labels: bool, no_inline_labels: bool, no_colorbar: bool, colorbar_label: String, number_format_cb: String, line_color: String, line_style: String, line_width: f64, fontsize_labels: f64, with_selected: bool, selected_level: f64, selected_line_color: String, selected_line_style: String, selected_line_width: f64, extra_filled: String, extra_line: String, buffer: String, }
72
73impl Contour {
74 pub fn new() -> Self {
76 Contour {
77 colors: Vec::new(),
78 levels: Vec::new(),
79 colormap_name: "bwr".to_string(),
80 no_lines: false,
81 no_labels: false,
82 no_inline_labels: false,
83 no_colorbar: false,
84 colorbar_label: String::new(),
85 number_format_cb: String::new(),
86 line_color: "black".to_string(),
87 line_style: String::new(),
88 line_width: 0.0,
89 fontsize_labels: 0.0,
90 with_selected: false,
91 selected_level: 0.0,
92 selected_line_color: "yellow".to_string(),
93 selected_line_style: "-".to_string(),
94 selected_line_width: 2.0,
95 extra_filled: String::new(),
96 extra_line: String::new(),
97 buffer: String::new(),
98 }
99 }
100
101 pub fn draw<'a, T, U>(&mut self, x: &'a T, y: &'a T, z: &'a T)
118 where
119 T: AsMatrix<'a, U>,
120 U: 'a + std::fmt::Display + Num,
121 {
122 matrix_to_array(&mut self.buffer, "x", x);
123 matrix_to_array(&mut self.buffer, "y", y);
124 matrix_to_array(&mut self.buffer, "z", z);
125 if self.colors.len() > 0 {
126 generate_list_quoted(&mut self.buffer, "colors", &self.colors);
127 }
128 if self.levels.len() > 0 {
129 vector_to_array(&mut self.buffer, "levels", &self.levels);
130 }
131 let opt = self.options_filled();
132 write!(&mut self.buffer, "cf=plt.contourf(x,y,z{})\n", &opt).unwrap();
133 if !self.no_lines {
134 let opt_line = self.options_line();
135 write!(&mut self.buffer, "cl=plt.contour(x,y,z{})\n", &opt_line).unwrap();
136 if !self.no_labels {
137 let opt_label = self.options_label();
138 write!(&mut self.buffer, "plt.clabel(cl{})\n", &opt_label).unwrap();
139 }
140 }
141 if !self.no_colorbar {
142 let opt_colorbar = self.options_colorbar();
143 write!(&mut self.buffer, "cb=plt.colorbar(cf{})\n", &opt_colorbar).unwrap();
144 if self.colorbar_label != "" {
145 write!(&mut self.buffer, "cb.ax.set_ylabel(r'{}')\n", self.colorbar_label).unwrap();
146 }
147 }
148 if self.with_selected {
149 let opt_selected = self.options_selected();
150 write!(&mut self.buffer, "plt.contour(x,y,z{})\n", &opt_selected).unwrap();
151 }
152 }
153
154 pub fn set_colors(&mut self, colors: &[&str]) -> &mut Self {
158 self.colors = colors.iter().map(|color| color.to_string()).collect();
159 self
160 }
161
162 pub fn set_levels(&mut self, levels: &[f64]) -> &mut Self {
164 self.levels = levels.to_vec();
165 self
166 }
167
168 pub fn set_colormap_index(&mut self, index: usize) -> &mut Self {
181 const CMAP: [&str; 7] = ["bwr", "RdBu", "hsv", "jet", "terrain", "pink", "Greys"];
182 self.colormap_name = CMAP[index % 7].to_string();
183 self.colors = Vec::new();
184 self
185 }
186
187 pub fn set_colormap_name(&mut self, name: &str) -> &mut Self {
195 self.colormap_name = String::from(name);
196 self.colors = Vec::new();
197 self
198 }
199
200 pub fn set_no_lines(&mut self, flag: bool) -> &mut Self {
202 self.no_lines = flag;
203 self
204 }
205
206 pub fn set_no_labels(&mut self, flag: bool) -> &mut Self {
208 self.no_labels = flag;
209 self
210 }
211
212 pub fn set_no_inline_labels(&mut self, flag: bool) -> &mut Self {
214 self.no_inline_labels = flag;
215 self
216 }
217
218 pub fn set_no_colorbar(&mut self, flag: bool) -> &mut Self {
220 self.no_colorbar = flag;
221 self
222 }
223
224 pub fn set_colorbar_label(&mut self, label: &str) -> &mut Self {
226 self.colorbar_label = String::from(label);
227 self
228 }
229
230 pub fn set_number_format_cb(&mut self, format: &str) -> &mut Self {
232 self.number_format_cb = String::from(format);
233 self
234 }
235
236 pub fn set_line_color(&mut self, color: &str) -> &mut Self {
238 self.line_color = String::from(color);
239 self
240 }
241
242 pub fn set_line_style(&mut self, style: &str) -> &mut Self {
248 self.line_style = String::from(style);
249 self
250 }
251
252 pub fn set_line_width(&mut self, width: f64) -> &mut Self {
254 self.line_width = width;
255 self
256 }
257
258 pub fn set_fontsize_labels(&mut self, fontsize: f64) -> &mut Self {
260 self.fontsize_labels = fontsize;
261 self
262 }
263
264 pub fn set_selected_level(&mut self, level: f64, enabled: bool) -> &mut Self {
268 self.selected_level = level;
269 self.with_selected = enabled;
270 self
271 }
272
273 pub fn set_selected_line_color(&mut self, color: &str) -> &mut Self {
275 self.selected_line_color = String::from(color);
276 self
277 }
278
279 pub fn set_selected_line_style(&mut self, style: &str) -> &mut Self {
285 self.selected_line_style = String::from(style);
286 self
287 }
288
289 pub fn set_selected_line_width(&mut self, width: f64) -> &mut Self {
291 self.selected_line_width = width;
292 self
293 }
294
295 pub fn set_extra_filled(&mut self, extra: &str) -> &mut Self {
305 self.extra_filled = extra.to_string();
306 self
307 }
308
309 pub fn set_extra_line(&mut self, extra: &str) -> &mut Self {
319 self.extra_line = extra.to_string();
320 self
321 }
322
323 fn options_filled(&self) -> String {
325 let mut opt = String::new();
326 if self.colors.len() > 0 {
327 write!(&mut opt, ",colors=colors",).unwrap();
328 } else {
329 if self.colormap_name != "" {
330 write!(&mut opt, ",cmap=plt.get_cmap('{}')", self.colormap_name).unwrap();
331 }
332 }
333 if self.levels.len() > 0 {
334 write!(&mut opt, ",levels=levels").unwrap();
335 }
336 if self.extra_filled != "" {
337 write!(&mut opt, ",{}", self.extra_filled).unwrap();
338 }
339 opt
340 }
341
342 fn options_line(&self) -> String {
344 let mut opt = String::new();
345 if self.line_color != "" {
346 write!(&mut opt, ",colors=['{}']", self.line_color).unwrap();
347 }
348 if self.levels.len() > 0 {
349 write!(&mut opt, ",levels=levels").unwrap();
350 }
351 if self.line_style != "" {
352 write!(&mut opt, ",linestyles=['{}']", self.line_style).unwrap();
353 }
354 if self.line_width > 0.0 {
355 write!(&mut opt, ",linewidths=[{}]", self.line_width).unwrap();
356 }
357 if self.extra_line != "" {
358 write!(&mut opt, ",{}", self.extra_line).unwrap();
359 }
360 opt
361 }
362
363 fn options_label(&self) -> String {
365 let mut opt = String::new();
366 if self.no_inline_labels {
367 write!(&mut opt, ",inline=False").unwrap();
368 } else {
369 write!(&mut opt, ",inline=True").unwrap();
370 }
371 if self.fontsize_labels > 0.0 {
372 write!(&mut opt, ",fontsize={}", self.fontsize_labels).unwrap();
373 }
374 opt
375 }
376
377 fn options_colorbar(&self) -> String {
379 let mut opt = String::new();
380 if self.number_format_cb != "" {
381 write!(&mut opt, ",format='{}'", self.number_format_cb).unwrap();
382 }
383 opt
384 }
385
386 fn options_selected(&self) -> String {
388 let mut opt = String::new();
389 if self.selected_line_color != "" {
390 write!(&mut opt, ",colors=['{}']", self.selected_line_color).unwrap();
391 }
392 write!(&mut opt, ",levels=[{}]", self.selected_level).unwrap();
393 if self.selected_line_style != "" {
394 write!(&mut opt, ",linestyles=['{}']", self.selected_line_style).unwrap();
395 }
396 if self.selected_line_width > 0.0 {
397 write!(&mut opt, ",linewidths=[{}]", self.selected_line_width).unwrap();
398 }
399 opt
400 }
401}
402
403impl GraphMaker for Contour {
404 fn get_buffer<'a>(&'a self) -> &'a String {
405 &self.buffer
406 }
407 fn clear_buffer(&mut self) {
408 self.buffer.clear();
409 }
410}
411
412#[cfg(test)]
415mod tests {
416 use super::Contour;
417 use crate::GraphMaker;
418
419 #[test]
420 fn new_works() {
421 let contour = Contour::new();
422 assert_eq!(contour.colors.len(), 0);
423 assert_eq!(contour.levels.len(), 0);
424 assert_eq!(contour.colormap_name, "bwr");
425 assert_eq!(contour.no_lines, false);
426 assert_eq!(contour.no_labels, false);
427 assert_eq!(contour.no_inline_labels, false);
428 assert_eq!(contour.no_colorbar, false);
429 assert_eq!(contour.colorbar_label.len(), 0);
430 assert_eq!(contour.number_format_cb.len(), 0);
431 assert_eq!(contour.line_color, "black".to_string());
432 assert_eq!(contour.line_style.len(), 0);
433 assert_eq!(contour.line_width, 0.0);
434 assert_eq!(contour.fontsize_labels, 0.0);
435 assert_eq!(contour.with_selected, false);
436 assert_eq!(contour.selected_level, 0.0);
437 assert_eq!(contour.selected_line_color, "yellow".to_string());
438 assert_eq!(contour.selected_line_style, "-".to_string());
439 assert_eq!(contour.selected_line_width, 2.0);
440 assert_eq!(contour.buffer.len(), 0);
441 }
442
443 #[test]
444 fn options_filled_works() {
445 let mut contour = Contour::new();
446 contour
447 .set_colors(&vec!["#f00", "#0f0", "#00f"])
448 .set_levels(&vec![0.25, 0.5, 1.0]);
449 let opt = contour.options_filled();
450 assert_eq!(
451 opt,
452 ",colors=colors\
453 ,levels=levels"
454 );
455 contour.set_colormap_index(4);
456 let opt = contour.options_filled();
457 assert_eq!(
458 opt,
459 ",cmap=plt.get_cmap('terrain')\
460 ,levels=levels"
461 );
462 }
463
464 #[test]
465 fn options_line_works() {
466 let mut contour = Contour::new();
467 contour
468 .set_levels(&vec![0.25, 0.5, 1.0])
469 .set_line_color("red")
470 .set_line_style(":")
471 .set_line_width(3.0);
472 let opt = contour.options_line();
473 assert_eq!(
474 opt,
475 ",colors=['red']\
476 ,levels=levels\
477 ,linestyles=[':']\
478 ,linewidths=[3]"
479 );
480 }
481
482 #[test]
483 fn options_label_works() {
484 let mut contour = Contour::new();
485 contour.set_no_inline_labels(false).set_fontsize_labels(5.0);
486 let opt = contour.options_label();
487 assert_eq!(
488 opt,
489 ",inline=True\
490 ,fontsize=5"
491 );
492 contour.set_no_inline_labels(true);
493 let opt = contour.options_label();
494 assert_eq!(
495 opt,
496 ",inline=False\
497 ,fontsize=5"
498 );
499 }
500
501 #[test]
502 fn options_colorbar_works() {
503 let mut contour = Contour::new();
504 contour.set_number_format_cb("%.4f");
505 let opt = contour.options_colorbar();
506 assert_eq!(opt, ",format='%.4f'");
507 }
508
509 #[test]
510 fn options_selected_works() {
511 let mut contour = Contour::new();
512 contour
513 .set_selected_level(0.75, true)
514 .set_selected_line_color("blue")
515 .set_selected_line_style("--")
516 .set_selected_line_width(2.5);
517 let opt = contour.options_selected();
518 assert_eq!(
519 opt,
520 ",colors=['blue']\
521 ,levels=[0.75]\
522 ,linestyles=['--']\
523 ,linewidths=[2.5]"
524 );
525 }
526
527 #[test]
528 fn draw_works() {
529 let mut contour = Contour::new();
530 contour
531 .set_colors(&vec!["#f00", "#0f0", "#00f"])
532 .set_levels(&vec![0.25, 0.5, 1.0])
533 .set_colorbar_label("temperature")
534 .set_selected_level(0.0, true);
535 let x = vec![vec![-0.5, 0.0, 0.5], vec![-0.5, 0.0, 0.5], vec![-0.5, 0.0, 0.5]];
536 let y = vec![vec![-0.5, -0.5, -0.5], vec![0.0, 0.0, 0.0], vec![0.5, 0.5, 0.5]];
537 let z = vec![vec![0.50, 0.25, 0.50], vec![0.25, 0.00, 0.25], vec![0.50, 0.25, 0.50]];
538 contour.draw(&x, &y, &z);
539 let b: &str = "x=np.array([[-0.5,0,0.5,],[-0.5,0,0.5,],[-0.5,0,0.5,],],dtype=float)\n\
540 y=np.array([[-0.5,-0.5,-0.5,],[0,0,0,],[0.5,0.5,0.5,],],dtype=float)\n\
541 z=np.array([[0.5,0.25,0.5,],[0.25,0,0.25,],[0.5,0.25,0.5,],],dtype=float)\n\
542 colors=['#f00','#0f0','#00f',]\n\
543 levels=np.array([0.25,0.5,1,],dtype=float)\n\
544 cf=plt.contourf(x,y,z,colors=colors,levels=levels)\n\
545 cl=plt.contour(x,y,z,colors=['black'],levels=levels)\n\
546 plt.clabel(cl,inline=True)\n\
547 cb=plt.colorbar(cf)\n\
548 cb.ax.set_ylabel(r'temperature')\n\
549 plt.contour(x,y,z,colors=['yellow'],levels=[0],linestyles=['-'],linewidths=[2])\n";
550 assert_eq!(contour.buffer, b);
551 contour.clear_buffer();
552 assert_eq!(contour.buffer, "");
553 }
554}