diff --git a/examples/README.md b/examples/README.md index 7bafeb6..9d4f905 100644 --- a/examples/README.md +++ b/examples/README.md @@ -174,6 +174,7 @@ Some output of integration tests are shown below. [test_stream.rs](https://github.com/cpmech/plotpy/tree/main/tests/test_stream.rs) ![streamplot_quiver](https://raw.githubusercontent.com/cpmech/plotpy/main/figures/integ_stream_arrows_1.svg) +![streamplot_quiver](https://raw.githubusercontent.com/cpmech/plotpy/main/figures/integ_stream_arrows_2.svg) ## Surface and wireframe diff --git a/figures/integ_stream_arrows_2.svg b/figures/integ_stream_arrows_2.svg new file mode 100644 index 0000000..fcd149c --- /dev/null +++ b/figures/integ_stream_arrows_2.svg @@ -0,0 +1,3639 @@ + + + + + + + + 2025-12-27T08:48:48.438297 + image/svg+xml + + + Matplotlib v3.6.3, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/stream.rs b/src/stream.rs index 6ea9a61..f42c1d8 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -1,6 +1,6 @@ use super::GraphMaker; -use crate::conversions::matrix_to_array; -use crate::AsMatrix; +use crate::conversions::{matrix_to_array, vector_to_array}; +use crate::{AsMatrix, AsVector}; use num_traits::Num; use std::fmt::Write; @@ -13,11 +13,13 @@ pub struct Stream { streamplot_linewidth: f64, streamplot_arrow_style: String, streamplot_density: f64, + streamplot_zorder: usize, streamplot_extra: String, // quiver options quiver_scale: f64, quiver_pivot: String, + quiver_zorder: usize, quiver_extra: String, // buffer @@ -34,10 +36,12 @@ impl Stream { streamplot_linewidth: 0.0, streamplot_arrow_style: String::new(), streamplot_density: 0.0, + streamplot_zorder: 0, streamplot_extra: String::new(), // quiver options quiver_scale: 0.0, quiver_pivot: String::new(), + quiver_zorder: 0, quiver_extra: String::new(), // extra options // buffer @@ -45,32 +49,85 @@ impl Stream { } } - /// Draws streamlines (stream plot) - pub fn draw<'a, T, U>(&mut self, xx: &'a T, yy: &'a T, dx: &'a T, dy: &'a T) + /// Draws streamlines (stream plot) given x,y matrices and u,v matrices + /// + /// From [Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.streamplot.html): + /// + /// * xx, yy -- 2D arrays. Evenly spaced strictly increasing arrays to make a grid. + /// All rows of *x* must be equal and all columns of *y* must be equal; i.e., + /// they must be as if generated by `np.meshgrid(x, y)`. + /// * uu, vv -- 2D arrays. *x* and *y*-velocities. The number of rows and columns + /// must match the length of *y* and *x*, respectively. + pub fn draw<'a, T, U>(&mut self, xx: &'a T, yy: &'a T, uu: &'a T, vv: &'a T) where T: AsMatrix<'a, U>, U: 'a + std::fmt::Display + Num, { matrix_to_array(&mut self.buffer, "xx", xx); matrix_to_array(&mut self.buffer, "yy", yy); - matrix_to_array(&mut self.buffer, "dx", dx); - matrix_to_array(&mut self.buffer, "dy", dy); + matrix_to_array(&mut self.buffer, "uu", uu); + matrix_to_array(&mut self.buffer, "vv", vv); + let opt = self.options_streamplot(); + write!(&mut self.buffer, "plt.streamplot(xx,yy,uu,vv{})\n", &opt).unwrap(); + } + + /// Draws streamlines (stream plot) given x,y vectors and u,v matrices + /// + /// From [Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.streamplot.html): + /// + /// * x, y -- 1D arrays. Evenly spaced strictly increasing arrays to make a grid. + /// * u, v -- 2D arrays. *x* and *y*-velocities. The number of rows and columns + /// must match the length of *y* and *x*, respectively. + pub fn draw_alt<'a, V, M, U>(&mut self, x: &'a V, y: &'a V, uu: &'a M, vv: &'a M) + where + V: AsVector<'a, U>, + M: AsMatrix<'a, U>, + U: 'a + std::fmt::Display + Num, + { + vector_to_array(&mut self.buffer, "x", x); + vector_to_array(&mut self.buffer, "y", y); + matrix_to_array(&mut self.buffer, "uu", uu); + matrix_to_array(&mut self.buffer, "vv", vv); let opt = self.options_streamplot(); - write!(&mut self.buffer, "plt.streamplot(xx,yy,dx,dy{})\n", &opt).unwrap(); + write!(&mut self.buffer, "plt.streamplot(x,y,uu,vv{})\n", &opt).unwrap(); } - /// Draws arrows (quiver plot) - pub fn draw_arrows<'a, T, U>(&mut self, xx: &'a T, yy: &'a T, dx: &'a T, dy: &'a T) + /// Draws arrows (quiver plot) given matrices + /// + /// From [Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.quiver.html): + /// + /// * xx, yy -- 2D arrays. The x and y coordinates of the arrow locations. + /// * uu, vv -- 2D arrays. The x and y direction components of the arrow vectors. + pub fn draw_arrows<'a, T, U>(&mut self, xx: &'a T, yy: &'a T, uu: &'a T, vv: &'a T) where T: AsMatrix<'a, U>, U: 'a + std::fmt::Display + Num, { matrix_to_array(&mut self.buffer, "xx", xx); matrix_to_array(&mut self.buffer, "yy", yy); - matrix_to_array(&mut self.buffer, "dx", dx); - matrix_to_array(&mut self.buffer, "dy", dy); + matrix_to_array(&mut self.buffer, "uu", uu); + matrix_to_array(&mut self.buffer, "vv", vv); + let opt = self.options_quiver(); + write!(&mut self.buffer, "plt.quiver(xx,yy,uu,vv{})\n", &opt).unwrap(); + } + + /// Draws arrows (quiver plot) given vectors + /// + /// From [Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.quiver.html): + /// + /// * x, y -- 1D arrays. The x and y coordinates of the arrow locations. + /// * u, v -- 1D arrays. The x and y direction components of the arrow vectors. + pub fn draw_arrows_alt<'a, T, U>(&mut self, x: &'a T, y: &'a T, u: &'a T, v: &'a T) + where + T: AsVector<'a, U>, + U: 'a + std::fmt::Display + Num, + { + vector_to_array(&mut self.buffer, "x", x); + vector_to_array(&mut self.buffer, "y", y); + vector_to_array(&mut self.buffer, "u", u); + vector_to_array(&mut self.buffer, "v", v); let opt = self.options_quiver(); - write!(&mut self.buffer, "plt.quiver(xx,yy,dx,dy{})\n", &opt).unwrap(); + write!(&mut self.buffer, "plt.quiver(x,y,u,v{})\n", &opt).unwrap(); } /// Sets the line color (quiver or streamlines) @@ -115,6 +172,12 @@ impl Stream { self } + /// Sets the z-order of streamlines + pub fn set_streamline_zorder(&mut self, zorder: usize) -> &mut Self { + self.streamplot_zorder = zorder; + self + } + /// Sets extra options for streamlines /// /// See @@ -139,6 +202,12 @@ impl Stream { self } + /// Sets the quiver z-order + pub fn set_quiver_zorder(&mut self, zorder: usize) -> &mut Self { + self.quiver_zorder = zorder; + self + } + /// Sets extra options for quiver /// /// See @@ -162,6 +231,9 @@ impl Stream { if self.streamplot_density > 0.0 { write!(&mut opt, ",density={}", self.streamplot_density).unwrap(); } + if self.streamplot_zorder > 0 { + write!(&mut opt, ",zorder={}", self.streamplot_zorder).unwrap(); + } if self.streamplot_extra != "" { write!(&mut opt, ",{}", self.streamplot_extra).unwrap(); } @@ -180,6 +252,9 @@ impl Stream { if self.quiver_pivot != "" { write!(&mut opt, ",pivot='{}'", self.quiver_pivot).unwrap(); } + if self.quiver_zorder > 0 { + write!(&mut opt, ",zorder={}", self.quiver_zorder).unwrap(); + } if self.quiver_extra != "" { write!(&mut opt, ",{}", self.quiver_extra).unwrap(); } diff --git a/tests/test_stream.rs b/tests/test_stream.rs index 0b7944d..fc10d4d 100644 --- a/tests/test_stream.rs +++ b/tests/test_stream.rs @@ -1,4 +1,4 @@ -use plotpy::{generate2d, Plot, StrError, Stream}; +use plotpy::{generate2d, linspace, Plot, StrError, Stream}; use std::fs::File; use std::io::{BufRead, BufReader}; use std::path::Path; @@ -14,13 +14,13 @@ fn test_stream_arrows_1() -> Result<(), StrError> { // data let (nx, ny) = (10, 10); let (xx, yy) = generate2d(-2.0, 2.0, -2.0, 2.0, nx, ny); - let (mut dx, mut dy) = generate2d(0.0, 1.0, 0.0, 1.0, nx, ny); + let (mut uu, mut vv) = generate2d(0.0, 1.0, 0.0, 1.0, nx, ny); for j in 0..ny { for i in 0..nx { let x = xx[j][i]; let y = yy[j][i]; - dx[j][i] = -y; - dy[j][i] = x; + uu[j][i] = -y; + vv[j][i] = x; } } @@ -31,12 +31,12 @@ fn test_stream_arrows_1() -> Result<(), StrError> { .set_streamplot_arrow_style("fancy") .set_streamplot_density(0.8) .set_streamplot_extra("broken_streamlines=False") - .draw(&xx, &yy, &dx, &dy); + .draw(&xx, &yy, &uu, &vv); quiver .set_color("#4752c7ff") .set_quiver_inv_scale(15.0) .set_quiver_pivot("mid") - .draw_arrows(&xx, &yy, &dx, &dy); + .draw_arrows(&xx, &yy, &uu, &vv); // add contour to plot let mut plot = Plot::new(); @@ -54,3 +54,42 @@ fn test_stream_arrows_1() -> Result<(), StrError> { assert!(n > 8650 && n < 8730); Ok(()) } + +#[test] +fn test_stream_arrows_2() -> Result<(), StrError> { + // object and options + let mut stream = Stream::new(); + let mut quiver = Stream::new(); + + // data + let n = 10; + let x = linspace(1.0, 2.0, n); + let y = x.clone(); + let u = linspace(1.0, 2.0, n); + let v = u.clone(); + let (uu, vv) = generate2d(1.0, 2.0, 1.0, 2.0, n, n); + + // draw arrows + stream.set_streamline_zorder(1).draw_alt(&x, &y, &uu, &vv); + quiver + .set_quiver_zorder(2) + .set_color("red") + .set_quiver_inv_scale(20.0) + .draw_arrows_alt(&x, &y, &u, &v); + + // add contour to plot + let mut plot = Plot::new(); + plot.add(&stream).add(&quiver); + + // save figure + let path = Path::new(OUT_DIR).join("integ_stream_arrows_2.svg"); + plot.set_equal_axes(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 > 3600 && n < 3680); + Ok(()) +}