From 17f679b67ab455b5f049010f495147a2a6f492ca Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Fri, 26 Dec 2025 09:08:01 +1000 Subject: [PATCH 1/2] Impl draw_tri method in Contour --- .vscode/settings.json | 2 + src/constants.rs | 3 +- src/contour.rs | 141 ++++++++++++++++++++++++++++++++++-------- tests/test_contour.rs | 57 +++++++++++++++++ 4 files changed, 177 insertions(+), 26 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 69d0337..ff995d8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -89,6 +89,8 @@ "TICKRIGHT", "TICKUP", "toolkits", + "tricontour", + "tricontourf", "triplot", "trisurf", "twinx", diff --git a/src/constants.rs b/src/constants.rs index 1a0a030..35754d4 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -34,6 +34,7 @@ import matplotlib.patheffects as pff import matplotlib.lines as lns import matplotlib.transforms as tra import mpl_toolkits.mplot3d +import matplotlib.tri as plt_tri # Variable to handle NaN values coming from Rust NaN = np.nan @@ -150,6 +151,6 @@ mod tests { #[test] fn constants_are_correct() { - assert_eq!(PYTHON_HEADER.len(), 2886); + assert_eq!(PYTHON_HEADER.len(), 2919); } } diff --git a/src/contour.rs b/src/contour.rs index 1a65e81..35e90c8 100644 --- a/src/contour.rs +++ b/src/contour.rs @@ -1,4 +1,5 @@ use super::{generate_list_quoted, matrix_to_array, vector_to_array, AsMatrix, GraphMaker}; +use crate::AsVector; use num_traits::Num; use std::fmt::Write; @@ -47,27 +48,32 @@ use std::fmt::Write; /// /// ![integ_contour.svg](https://raw.githubusercontent.com/cpmech/plotpy/main/figures/integ_contour.svg) pub struct Contour { - colors: Vec, // Colors to be used instead of colormap - levels: Vec, // Pre-defined levels - colormap_name: String, // Colormap name - no_lines: bool, // Skip drawing a lines contour - no_labels: bool, // Skip adding labels to the lines contour - no_inline_labels: bool, // Do not draw labels inline - no_colorbar: bool, // Skip drawing a colorbar - colorbar_label: String, // Colorbar label - number_format_cb: String, // Number format for the labels in lines contour - line_color: String, // Line color for the lines contour - line_style: String, // Line style for the lines contour - line_width: f64, // Line width for the lines contour - fontsize_labels: f64, // Font size for labels - with_selected: bool, // Draw a line contour with a selected level - selected_level: f64, // Selected level (e.g., 0.0) - selected_line_color: String, // Color to mark the selected level - selected_line_style: String, // Line style for the selected level - selected_line_width: f64, // Line width for the selected level - extra_filled: String, // Extra commands (comma separated) for the filled contour - extra_line: String, // Extra commands (comma separated) for the line contour - buffer: String, // buffer + colors: Vec, // Colors to be used instead of colormap + levels: Vec, // Pre-defined levels + colormap_name: String, // Colormap name + no_fill: bool, // Skip drawing filled contour + no_lines: bool, // Skip drawing a lines contour + no_labels: bool, // Skip adding labels to the lines contour + no_inline_labels: bool, // Do not draw labels inline + no_colorbar: bool, // Skip drawing a colorbar + colorbar_label: String, // Colorbar label + number_format_cb: String, // Number format for the labels in lines contour + line_color: String, // Line color for the lines contour + line_style: String, // Line style for the lines contour + line_width: f64, // Line width for the lines contour + fontsize_labels: f64, // Font size for labels + with_selected: bool, // Draw a line contour with a selected level + selected_level: f64, // Selected level (e.g., 0.0) + selected_line_color: String, // Color to mark the selected level + selected_line_style: String, // Line style for the selected level + selected_line_width: f64, // Line width for the selected level + extra_filled: String, // Extra commands (comma separated) for the filled contour + extra_line: String, // Extra commands (comma separated) for the line contour + tri_show_edges: bool, // Show triangulation edges + tri_edges_color: String, // Triangulation edges color + tri_edges_line_width: f64, // Triangulation edges line width + tri_edges_line_style: String, // Triangulation edges line style + buffer: String, // buffer } impl Contour { @@ -77,6 +83,7 @@ impl 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, @@ -94,6 +101,10 @@ impl Contour { 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(), } } @@ -110,6 +121,7 @@ impl Contour { /// /// The following flags control what features are not to be drawn: /// + /// * `no_fill` -- skip drawing filled contour /// * `no_lines` -- skip drawing a lines contour on top of the filled contour /// * `no_labels` -- skip adding labels to the lines contour (if enabled) /// * `no_colorbar` -- skip drawing a colorbar @@ -122,6 +134,53 @@ impl Contour { 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); + } + + /// Draws a fancy contour using a triangulation: filled contour with a line contour and a colorbar + /// + /// # Input + /// + /// * `x` -- list of x coordinates + /// * `y` -- list of y coordinates + /// * `z` -- list of elevations at each coordinate + /// * `connectivity` -- triangulation/connectivity + /// + /// # Flags + /// + /// The following flags control what features are not to be drawn: + /// + /// * `no_fill` -- skip drawing filled contour + /// * `no_lines` -- skip drawing a lines contour on top of the filled contour + /// * `no_labels` -- skip adding labels to the lines contour (if enabled) + /// * `no_colorbar` -- skip drawing a colorbar + /// * `with_selected` -- draw a line contour with a selected level (e.g., 0.0) on top of everything + 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(); + } + } + + /// Draw contour or tricontour + 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); } @@ -129,16 +188,18 @@ impl Contour { vector_to_array(&mut self.buffer, "levels", &self.levels); } let opt = self.options_filled(); - write!(&mut self.buffer, "cf=plt.contourf(x,y,z{})\n", &opt).unwrap(); + 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.contour(x,y,z{})\n", &opt_line).unwrap(); + 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 { + if !self.no_colorbar && !self.no_fill { let opt_colorbar = self.options_colorbar(); write!(&mut self.buffer, "cb=plt.colorbar(cf{})\n", &opt_colorbar).unwrap(); if self.colorbar_label != "" { @@ -147,7 +208,7 @@ impl Contour { } if self.with_selected { let opt_selected = self.options_selected(); - write!(&mut self.buffer, "plt.contour(x,y,z{})\n", &opt_selected).unwrap(); + write!(&mut self.buffer, "plt.{},z{})\n", contour, &opt_selected).unwrap(); } } @@ -197,6 +258,12 @@ impl Contour { self } + /// Sets option to skip drawing filled contour + pub fn set_no_fill(&mut self, flag: bool) -> &mut Self { + self.no_fill = flag; + self + } + /// Sets option to skip drawing a lines contour on top of the filled contour pub fn set_no_lines(&mut self, flag: bool) -> &mut Self { self.no_lines = flag; @@ -320,6 +387,30 @@ impl Contour { self } + /// Sets option to show triangulation edges (only for draw_tri) + pub fn set_tri_show_edges(&mut self, flag: bool) -> &mut Self { + self.tri_show_edges = flag; + self + } + + /// Sets triangulation edges color (only for draw_tri) + pub fn set_tri_edges_color(&mut self, color: &str) -> &mut Self { + self.tri_edges_color = color.to_string(); + self + } + + /// Sets triangulation edges line width (only for draw_tri) + pub fn set_tri_edges_line_width(&mut self, width: f64) -> &mut Self { + self.tri_edges_line_width = width; + self + } + + /// Sets triangulation edges line style (only for draw_tri) + pub fn set_tri_edges_line_style(&mut self, style: &str) -> &mut Self { + self.tri_edges_line_style = style.to_string(); + self + } + /// Returns options for filled contour fn options_filled(&self) -> String { let mut opt = String::new(); diff --git a/tests/test_contour.rs b/tests/test_contour.rs index 526b54c..1e099c8 100644 --- a/tests/test_contour.rs +++ b/tests/test_contour.rs @@ -146,3 +146,60 @@ fn test_contour_colormap_name() -> Result<(), StrError> { } Ok(()) } + +#[test] +fn test_contour_draw_tri() -> Result<(), StrError> { + // point coordinates (two triangles in a square) + + let x = vec![0.0, 1.0, 2.0, 3.0, 0.5, 1.5, 2.5, 1.0, 2.0, 1.5]; + let y = vec![0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 3.0]; + let connectivity = vec![ + vec![0, 1, 4], + vec![1, 5, 4], + vec![2, 6, 5], + vec![4, 5, 7], + vec![5, 8, 7], + vec![7, 8, 9], + vec![1, 2, 5], + vec![2, 3, 6], + ]; + + // elevations + let mut z = vec![0.0; x.len()]; + for i in 0..x.len() { + z[i] = x[i] + y[i]; + } + + // canvas + let mut contour = Contour::new(); + contour + .set_no_fill(false) + .set_colors(&vec!["#fcaeae", "#da98d1", "#c45178", "#5594d2", "#e6af69", "#e6d969"]) + .set_colorbar_label("x + y") + .set_line_color("black") + .set_line_style("-") + .set_line_width(1.5) + .set_tri_show_edges(true) + .set_tri_edges_color("green") + .set_tri_edges_line_width(1.0) + .set_tri_edges_line_style("--"); + + // draw contour + contour.draw_tri(&x, &y, &z, &connectivity); + + // add contour to plot + let mut plot = Plot::new(); + plot.add(&contour); + + // save figure + let path = Path::new(OUT_DIR).join("integ_contour_draw_tri.svg"); + plot.set_equal_axes(true).set_show_errors(true).save(&path)?; + + // check number of lines + let file = File::open(path).map_err(|_| "cannot open file")?; + let buffered = BufReader::new(file); + let lines_iter = buffered.lines(); + let n = lines_iter.count(); + assert!(n > 1030 && n < 1090); + Ok(()) +} From e748f603d5cddcece36d2214038cc3fbf33903f0 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Fri, 26 Dec 2025 09:09:54 +1000 Subject: [PATCH 2/2] Add figure --- examples/README.md | 1 + figures/integ_contour_draw_tri.svg | 1064 ++++++++++++++++++++++++++++ 2 files changed, 1065 insertions(+) create mode 100644 figures/integ_contour_draw_tri.svg diff --git a/examples/README.md b/examples/README.md index f6a29a0..37da89a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -78,6 +78,7 @@ Some output of integration tests are shown below. ![contour](https://raw.githubusercontent.com/cpmech/plotpy/main/figures/integ_contour_colormap_tab20c.svg) ![contour](https://raw.githubusercontent.com/cpmech/plotpy/main/figures/integ_contour_colors.svg) ![contour](https://raw.githubusercontent.com/cpmech/plotpy/main/figures/integ_contour.svg) +![contour](https://raw.githubusercontent.com/cpmech/plotpy/main/figures/integ_contour_draw_tri.svg) ## Curve diff --git a/figures/integ_contour_draw_tri.svg b/figures/integ_contour_draw_tri.svg new file mode 100644 index 0000000..76ae2f1 --- /dev/null +++ b/figures/integ_contour_draw_tri.svg @@ -0,0 +1,1064 @@ + + + + + + + + 2025-12-26T09:06:20.736188 + image/svg+xml + + + Matplotlib v3.6.3, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +