use super::{generate_list_quoted, matrix_to_array, vector_to_array, AsMatrix, GraphMaker};
use crate::AsVector;
use num_traits::Num;
use std::fmt::Write;
pub struct Contour {
colors: Vec<String>, levels: Vec<f64>, colormap_name: String, no_fill: bool, no_lines: bool, no_labels: bool, no_inline_labels: bool, no_colorbar: bool, colorbar_label: String, colorbar_axes: String, colorbar_location: String, colorbar_extra: String, number_format_cb: String, fill_alpha: Option<f64>, line_alpha: Option<f64>, 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, tri_show_edges: bool, tri_edges_color: String, tri_edges_line_width: f64, tri_edges_line_style: String, buffer: String, }
impl Contour {
pub fn new() -> Self {
Contour {
colors: Vec::new(),
levels: Vec::new(),
colormap_name: "bwr".to_string(),
no_fill: false,
no_lines: false,
no_labels: false,
no_inline_labels: false,
no_colorbar: false,
colorbar_label: String::new(),
colorbar_axes: String::new(),
colorbar_location: String::new(),
colorbar_extra: String::new(),
number_format_cb: String::new(),
fill_alpha: None,
line_alpha: None,
line_color: "black".to_string(),
line_style: String::new(),
line_width: 0.0,
fontsize_labels: 0.0,
with_selected: false,
selected_level: 0.0,
selected_line_color: "yellow".to_string(),
selected_line_style: "-".to_string(),
selected_line_width: 2.0,
extra_filled: String::new(),
extra_line: String::new(),
tri_show_edges: false,
tri_edges_color: "black".to_string(),
tri_edges_line_width: 0.5,
tri_edges_line_style: "-".to_string(),
buffer: String::new(),
}
}
pub fn draw<'a, T, U>(&mut self, x: &'a T, y: &'a T, z: &'a T)
where
T: AsMatrix<'a, U>,
U: 'a + std::fmt::Display + Num,
{
matrix_to_array(&mut self.buffer, "x", x);
matrix_to_array(&mut self.buffer, "y", y);
matrix_to_array(&mut self.buffer, "z", z);
self.contour_or_tricontour(false);
}
pub fn draw_tri<'a, T, U, C>(&mut self, x: &'a T, y: &'a T, z: &'a T, connectivity: &'a C)
where
T: AsVector<'a, U>,
U: 'a + std::fmt::Display + Num,
C: AsMatrix<'a, usize>,
{
vector_to_array(&mut self.buffer, "x", x);
vector_to_array(&mut self.buffer, "y", y);
vector_to_array(&mut self.buffer, "z", z);
matrix_to_array(&mut self.buffer, "con", connectivity);
write!(&mut self.buffer, "tri=plt_tri.Triangulation(x,y,triangles=con)\n").unwrap();
self.contour_or_tricontour(true);
if self.tri_show_edges {
write!(
&mut self.buffer,
"plt.triplot(tri,color='{}',lw={},ls='{}')\n",
self.tri_edges_color, self.tri_edges_line_width, self.tri_edges_line_style
)
.unwrap();
}
}
fn contour_or_tricontour(&mut self, tricontour: bool) {
let contour = if tricontour { "tricontour(tri" } else { "contour(x,y" };
let contourf = if tricontour { "tricontourf(tri" } else { "contourf(x,y" };
if self.colors.len() > 0 {
generate_list_quoted(&mut self.buffer, "colors", &self.colors);
}
if self.levels.len() > 0 {
vector_to_array(&mut self.buffer, "levels", &self.levels);
}
let opt = self.options_filled();
if !self.no_fill {
write!(&mut self.buffer, "cf=plt.{},z{})\n", contourf, &opt).unwrap();
}
if !self.no_lines {
let opt_line = self.options_line();
write!(&mut self.buffer, "cl=plt.{},z{})\n", contour, &opt_line).unwrap();
if !self.no_labels {
let opt_label = self.options_label();
write!(&mut self.buffer, "plt.clabel(cl{})\n", &opt_label).unwrap();
}
}
if !self.no_colorbar && !self.no_fill {
let opt_colorbar = self.options_colorbar();
if self.colorbar_axes != "" {
write!(&mut self.buffer, "{}", self.colorbar_axes).unwrap();
write!(&mut self.buffer, "cb=plt.colorbar(cf,cax=cax{})\n", &opt_colorbar).unwrap();
} else {
write!(&mut self.buffer, "cb=plt.colorbar(cf{})\n", &opt_colorbar).unwrap();
}
if self.colorbar_label != "" {
write!(&mut self.buffer, "cb.ax.set_ylabel(r'{}')\n", self.colorbar_label).unwrap();
}
}
if self.with_selected {
let opt_selected = self.options_selected();
write!(&mut self.buffer, "plt.{},z{})\n", contour, &opt_selected).unwrap();
}
}
pub fn set_colors(&mut self, colors: &[&str]) -> &mut Self {
self.colors = colors.iter().map(|color| color.to_string()).collect();
self
}
pub fn set_levels(&mut self, levels: &[f64]) -> &mut Self {
self.levels = levels.to_vec();
self
}
pub fn set_colormap_index(&mut self, index: usize) -> &mut Self {
const CMAP: [&str; 7] = ["bwr", "RdBu", "hsv", "jet", "terrain", "pink", "Greys"];
self.colormap_name = CMAP[index % 7].to_string();
self.colors = Vec::new();
self
}
pub fn set_colormap_name(&mut self, name: &str) -> &mut Self {
self.colormap_name = String::from(name);
self.colors = Vec::new();
self
}
pub fn set_no_fill(&mut self, flag: bool) -> &mut Self {
self.no_fill = flag;
self
}
pub fn set_no_lines(&mut self, flag: bool) -> &mut Self {
self.no_lines = flag;
self
}
pub fn set_no_labels(&mut self, flag: bool) -> &mut Self {
self.no_labels = flag;
self
}
pub fn set_no_inline_labels(&mut self, flag: bool) -> &mut Self {
self.no_inline_labels = flag;
self
}
pub fn set_no_colorbar(&mut self, flag: bool) -> &mut Self {
self.no_colorbar = flag;
self
}
pub fn set_colorbar_label(&mut self, label: &str) -> &mut Self {
self.colorbar_label = String::from(label);
self
}
pub fn set_colorbar_axes(&mut self, location: &str, width_pct: f64, pad: f64) -> &mut Self {
self.colorbar_axes = format!(
"ax=plt.gca()\n\
divider=make_axes_locatable(ax)\n\
cax=divider.append_axes('{}',size='{}%',pad={})\n\
plt.sca(ax)\n",
location, width_pct, pad
);
self
}
pub fn set_colorbar_location(&mut self, location: &str) -> &mut Self {
self.colorbar_location = location.to_string();
self
}
pub fn set_colorbar_extra(&mut self, extra: &str) -> &mut Self {
self.colorbar_extra = String::from(extra);
self
}
pub fn set_number_format_cb(&mut self, format: &str) -> &mut Self {
self.number_format_cb = String::from(format);
self
}
pub fn set_fill_alpha(&mut self, alpha: f64) -> &mut Self {
self.fill_alpha = Some(alpha);
self
}
pub fn set_line_alpha(&mut self, alpha: f64) -> &mut Self {
self.line_alpha = Some(alpha);
self
}
pub fn set_line_color(&mut self, color: &str) -> &mut Self {
self.line_color = String::from(color);
self
}
pub fn set_line_style(&mut self, style: &str) -> &mut Self {
self.line_style = String::from(style);
self
}
pub fn set_line_width(&mut self, width: f64) -> &mut Self {
self.line_width = width;
self
}
pub fn set_fontsize_labels(&mut self, fontsize: f64) -> &mut Self {
self.fontsize_labels = fontsize;
self
}
pub fn set_selected_level(&mut self, level: f64, enabled: bool) -> &mut Self {
self.selected_level = level;
self.with_selected = enabled;
self
}
pub fn set_selected_line_color(&mut self, color: &str) -> &mut Self {
self.selected_line_color = String::from(color);
self
}
pub fn set_selected_line_style(&mut self, style: &str) -> &mut Self {
self.selected_line_style = String::from(style);
self
}
pub fn set_selected_line_width(&mut self, width: f64) -> &mut Self {
self.selected_line_width = width;
self
}
pub fn set_extra_filled(&mut self, extra: &str) -> &mut Self {
self.extra_filled = extra.to_string();
self
}
pub fn set_extra_line(&mut self, extra: &str) -> &mut Self {
self.extra_line = extra.to_string();
self
}
pub fn set_tri_show_edges(&mut self, flag: bool) -> &mut Self {
self.tri_show_edges = flag;
self
}
pub fn set_tri_edges_color(&mut self, color: &str) -> &mut Self {
self.tri_edges_color = color.to_string();
self
}
pub fn set_tri_edges_line_width(&mut self, width: f64) -> &mut Self {
self.tri_edges_line_width = width;
self
}
pub fn set_tri_edges_line_style(&mut self, style: &str) -> &mut Self {
self.tri_edges_line_style = style.to_string();
self
}
fn options_filled(&self) -> String {
let mut opt = String::new();
if let Some(a) = self.fill_alpha {
write!(&mut opt, ",alpha={}", a).unwrap();
}
if self.colors.len() > 0 {
write!(&mut opt, ",colors=colors",).unwrap();
} else {
if self.colormap_name != "" {
write!(&mut opt, ",cmap=plt.get_cmap('{}')", self.colormap_name).unwrap();
}
}
if self.levels.len() > 0 {
write!(&mut opt, ",levels=levels").unwrap();
}
if self.extra_filled != "" {
write!(&mut opt, ",{}", self.extra_filled).unwrap();
}
opt
}
fn options_line(&self) -> String {
let mut opt = String::new();
if let Some(a) = self.line_alpha {
write!(&mut opt, ",alpha={}", a).unwrap();
}
if self.line_color != "" {
write!(&mut opt, ",colors=['{}']", self.line_color).unwrap();
}
if self.levels.len() > 0 {
write!(&mut opt, ",levels=levels").unwrap();
}
if self.line_style != "" {
write!(&mut opt, ",linestyles=['{}']", self.line_style).unwrap();
}
if self.line_width > 0.0 {
write!(&mut opt, ",linewidths=[{}]", self.line_width).unwrap();
}
if self.extra_line != "" {
write!(&mut opt, ",{}", self.extra_line).unwrap();
}
opt
}
fn options_label(&self) -> String {
let mut opt = String::new();
if self.no_inline_labels {
write!(&mut opt, ",inline=False").unwrap();
} else {
write!(&mut opt, ",inline=True").unwrap();
}
if self.fontsize_labels > 0.0 {
write!(&mut opt, ",fontsize={}", self.fontsize_labels).unwrap();
}
opt
}
fn options_colorbar(&self) -> String {
let mut opt = String::new();
if self.number_format_cb != "" {
write!(&mut opt, ",format='{}'", self.number_format_cb).unwrap();
}
if self.colorbar_location != "" {
write!(&mut opt, ",location='{}'", self.colorbar_location).unwrap();
}
if self.colorbar_extra != "" {
write!(&mut opt, ",{}", self.colorbar_extra).unwrap();
}
opt
}
fn options_selected(&self) -> String {
let mut opt = String::new();
if self.selected_line_color != "" {
write!(&mut opt, ",colors=['{}']", self.selected_line_color).unwrap();
}
write!(&mut opt, ",levels=[{}]", self.selected_level).unwrap();
if self.selected_line_style != "" {
write!(&mut opt, ",linestyles=['{}']", self.selected_line_style).unwrap();
}
if self.selected_line_width > 0.0 {
write!(&mut opt, ",linewidths=[{}]", self.selected_line_width).unwrap();
}
opt
}
}
impl GraphMaker for Contour {
fn get_buffer<'a>(&'a self) -> &'a String {
&self.buffer
}
fn clear_buffer(&mut self) {
self.buffer.clear();
}
}
#[cfg(test)]
mod tests {
use super::Contour;
use crate::GraphMaker;
#[test]
fn new_works() {
let contour = Contour::new();
assert_eq!(contour.colors.len(), 0);
assert_eq!(contour.levels.len(), 0);
assert_eq!(contour.colormap_name, "bwr");
assert_eq!(contour.no_lines, false);
assert_eq!(contour.no_labels, false);
assert_eq!(contour.no_inline_labels, false);
assert_eq!(contour.no_colorbar, false);
assert_eq!(contour.colorbar_label.len(), 0);
assert_eq!(contour.number_format_cb.len(), 0);
assert_eq!(contour.line_color, "black".to_string());
assert_eq!(contour.line_style.len(), 0);
assert_eq!(contour.line_width, 0.0);
assert_eq!(contour.fontsize_labels, 0.0);
assert_eq!(contour.with_selected, false);
assert_eq!(contour.selected_level, 0.0);
assert_eq!(contour.selected_line_color, "yellow".to_string());
assert_eq!(contour.selected_line_style, "-".to_string());
assert_eq!(contour.selected_line_width, 2.0);
assert_eq!(contour.buffer.len(), 0);
}
#[test]
fn options_filled_works() {
let mut contour = Contour::new();
contour
.set_colors(&vec!["#f00", "#0f0", "#00f"])
.set_levels(&vec![0.25, 0.5, 1.0]);
let opt = contour.options_filled();
assert_eq!(
opt,
",colors=colors\
,levels=levels"
);
contour.set_colormap_index(4);
let opt = contour.options_filled();
assert_eq!(
opt,
",cmap=plt.get_cmap('terrain')\
,levels=levels"
);
}
#[test]
fn options_line_works() {
let mut contour = Contour::new();
contour
.set_levels(&vec![0.25, 0.5, 1.0])
.set_line_color("red")
.set_line_style(":")
.set_line_width(3.0);
let opt = contour.options_line();
assert_eq!(
opt,
",colors=['red']\
,levels=levels\
,linestyles=[':']\
,linewidths=[3]"
);
}
#[test]
fn options_label_works() {
let mut contour = Contour::new();
contour.set_no_inline_labels(false).set_fontsize_labels(5.0);
let opt = contour.options_label();
assert_eq!(
opt,
",inline=True\
,fontsize=5"
);
contour.set_no_inline_labels(true);
let opt = contour.options_label();
assert_eq!(
opt,
",inline=False\
,fontsize=5"
);
}
#[test]
fn options_colorbar_works() {
let mut contour = Contour::new();
contour.set_number_format_cb("%.4f");
let opt = contour.options_colorbar();
assert_eq!(opt, ",format='%.4f'");
}
#[test]
fn options_selected_works() {
let mut contour = Contour::new();
contour
.set_selected_level(0.75, true)
.set_selected_line_color("blue")
.set_selected_line_style("--")
.set_selected_line_width(2.5);
let opt = contour.options_selected();
assert_eq!(
opt,
",colors=['blue']\
,levels=[0.75]\
,linestyles=['--']\
,linewidths=[2.5]"
);
}
#[test]
fn draw_works() {
let mut contour = Contour::new();
contour
.set_colors(&vec!["#f00", "#0f0", "#00f"])
.set_levels(&vec![0.25, 0.5, 1.0])
.set_colorbar_label("temperature")
.set_selected_level(0.0, true);
let x = vec![vec![-0.5, 0.0, 0.5], vec![-0.5, 0.0, 0.5], vec![-0.5, 0.0, 0.5]];
let y = vec![vec![-0.5, -0.5, -0.5], vec![0.0, 0.0, 0.0], vec![0.5, 0.5, 0.5]];
let z = vec![vec![0.50, 0.25, 0.50], vec![0.25, 0.00, 0.25], vec![0.50, 0.25, 0.50]];
contour.draw(&x, &y, &z);
let b: &str = "x=np.array([[-0.5,0,0.5,],[-0.5,0,0.5,],[-0.5,0,0.5,],])\n\
y=np.array([[-0.5,-0.5,-0.5,],[0,0,0,],[0.5,0.5,0.5,],])\n\
z=np.array([[0.5,0.25,0.5,],[0.25,0,0.25,],[0.5,0.25,0.5,],])\n\
colors=['#f00','#0f0','#00f',]\n\
levels=np.array([0.25,0.5,1,])\n\
cf=plt.contourf(x,y,z,colors=colors,levels=levels)\n\
cl=plt.contour(x,y,z,colors=['black'],levels=levels)\n\
plt.clabel(cl,inline=True)\n\
cb=plt.colorbar(cf)\n\
cb.ax.set_ylabel(r'temperature')\n\
plt.contour(x,y,z,colors=['yellow'],levels=[0],linestyles=['-'],linewidths=[2])\n";
assert_eq!(contour.buffer, b);
contour.clear_buffer();
assert_eq!(contour.buffer, "");
}
}