diff --git a/examples/README.md b/examples/README.md
index 37da89a..7bafeb6 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -19,6 +19,7 @@ Some output of integration tests are shown below.
- [Plot](#plot)
- [Subplot and GridSpec](#subplot-and-gridspec)
- [Slope icon](#slope-icon)
+- [Streamplot and quiver](#streamplot-and-quiver)
- [Surface and wireframe](#surface-and-wireframe)
- [Text](#text)
@@ -168,6 +169,12 @@ Some output of integration tests are shown below.


+## Streamplot and quiver
+
+[test_stream.rs](https://github.com/cpmech/plotpy/tree/main/tests/test_stream.rs)
+
+
+
## Surface and wireframe
[test_surface_geometry.rs](https://github.com/cpmech/plotpy/tree/main/tests/test_surface_geometry.rs)
diff --git a/figures/integ_stream_arrows_1.svg b/figures/integ_stream_arrows_1.svg
new file mode 100644
index 0000000..4e260b3
--- /dev/null
+++ b/figures/integ_stream_arrows_1.svg
@@ -0,0 +1,8701 @@
+
+
+
diff --git a/src/lib.rs b/src/lib.rs
index e6b590c..cec1ed0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -91,6 +91,7 @@ mod inset_axes;
mod legend;
mod plot;
mod slope_icon;
+mod stream;
mod super_title_params;
mod surface;
mod surface_geometry;
@@ -115,6 +116,7 @@ pub use inset_axes::*;
pub use legend::*;
pub use plot::*;
pub use slope_icon::*;
+pub use stream::*;
pub use super_title_params::*;
pub use surface::*;
pub use text::*;
diff --git a/src/stream.rs b/src/stream.rs
new file mode 100644
index 0000000..6ea9a61
--- /dev/null
+++ b/src/stream.rs
@@ -0,0 +1,202 @@
+use super::GraphMaker;
+use crate::conversions::matrix_to_array;
+use crate::AsMatrix;
+use num_traits::Num;
+use std::fmt::Write;
+
+/// Implements functions to illustrate vector fields using streamlines and quiver plots
+pub struct Stream {
+ // common options
+ color: String,
+
+ // streamplot options
+ streamplot_linewidth: f64,
+ streamplot_arrow_style: String,
+ streamplot_density: f64,
+ streamplot_extra: String,
+
+ // quiver options
+ quiver_scale: f64,
+ quiver_pivot: String,
+ quiver_extra: String,
+
+ // buffer
+ buffer: String,
+}
+
+impl Stream {
+ /// Creates a new Stream object
+ pub fn new() -> Self {
+ Stream {
+ // common options
+ color: String::new(),
+ // streamplot options
+ streamplot_linewidth: 0.0,
+ streamplot_arrow_style: String::new(),
+ streamplot_density: 0.0,
+ streamplot_extra: String::new(),
+ // quiver options
+ quiver_scale: 0.0,
+ quiver_pivot: String::new(),
+ quiver_extra: String::new(),
+ // extra options
+ // buffer
+ buffer: String::new(),
+ }
+ }
+
+ /// Draws streamlines (stream plot)
+ pub fn draw<'a, T, U>(&mut self, xx: &'a T, yy: &'a T, dx: &'a T, dy: &'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);
+ let opt = self.options_streamplot();
+ write!(&mut self.buffer, "plt.streamplot(xx,yy,dx,dy{})\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)
+ 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);
+ let opt = self.options_quiver();
+ write!(&mut self.buffer, "plt.quiver(xx,yy,dx,dy{})\n", &opt).unwrap();
+ }
+
+ /// Sets the line color (quiver or streamlines)
+ pub fn set_color(&mut self, color: &str) -> &mut Self {
+ self.color = String::from(color);
+ self
+ }
+
+ /// Sets the line width of streamlines
+ pub fn set_streamline_linewidth(&mut self, width: f64) -> &mut Self {
+ self.streamplot_linewidth = width;
+ self
+ }
+
+ /// Sets the arrow style
+ ///
+ /// Options:
+ ///
+ /// * "`-`" -- Curve : None
+ /// * "`->`" -- CurveB : head_length=0.4,head_width=0.2
+ /// * "`-[`" -- BracketB : widthB=1.0,lengthB=0.2,angleB=None
+ /// * "`-|>`" -- CurveFilledB : head_length=0.4,head_width=0.2
+ /// * "`<-`" -- CurveA : head_length=0.4,head_width=0.2
+ /// * "`<->`" -- CurveAB : head_length=0.4,head_width=0.2
+ /// * "`<|-`" -- CurveFilledA : head_length=0.4,head_width=0.2
+ /// * "`<|-|>`" -- CurveFilledAB : head_length=0.4,head_width=0.2
+ /// * "`]-`" -- BracketA : widthA=1.0,lengthA=0.2,angleA=None
+ /// * "`]-[`" -- BracketAB : widthA=1.0,lengthA=0.2,angleA=None,widthB=1.0,lengthB=0.2,angleB=None
+ /// * "`fancy`" -- Fancy : head_length=0.4,head_width=0.4,tail_width=0.4
+ /// * "`simple`" -- Simple : head_length=0.5,head_width=0.5,tail_width=0.2
+ /// * "`wedge`" -- Wedge : tail_width=0.3,shrink_factor=0.5
+ /// * "`|-|`" -- BarAB : widthA=1.0,angleA=None,widthB=1.0,angleB=None
+ /// * As defined in
+ pub fn set_streamplot_arrow_style(&mut self, style: &str) -> &mut Self {
+ self.streamplot_arrow_style = String::from(style);
+ self
+ }
+
+ /// Sets the density of streamlines
+ pub fn set_streamplot_density(&mut self, density: f64) -> &mut Self {
+ self.streamplot_density = density;
+ self
+ }
+
+ /// Sets extra options for streamlines
+ ///
+ /// See
+ pub fn set_streamplot_extra(&mut self, extra: &str) -> &mut Self {
+ self.streamplot_extra = extra.to_string();
+ self
+ }
+
+ /// Sets the quiver inverse scale
+ pub fn set_quiver_inv_scale(&mut self, scale: f64) -> &mut Self {
+ self.quiver_scale = scale;
+ self
+ }
+
+ /// Sets the quiver pivot
+ ///
+ /// Options: 'tail', 'mid', 'middle', 'tip'
+ ///
+ /// Default = 'tail'
+ pub fn set_quiver_pivot(&mut self, pivot: &str) -> &mut Self {
+ self.quiver_pivot = String::from(pivot);
+ self
+ }
+
+ /// Sets extra options for quiver
+ ///
+ /// See
+ pub fn set_quiver_extra(&mut self, extra: &str) -> &mut Self {
+ self.quiver_extra = extra.to_string();
+ self
+ }
+
+ /// Returns options for streamplot
+ fn options_streamplot(&self) -> String {
+ let mut opt = String::new();
+ if self.color != "" {
+ write!(&mut opt, ",color='{}'", self.color).unwrap();
+ }
+ if self.streamplot_linewidth > 0.0 {
+ write!(&mut opt, ",linewidth={}", self.streamplot_linewidth).unwrap();
+ }
+ if self.streamplot_arrow_style != "" {
+ write!(&mut opt, ",arrowstyle='{}'", self.streamplot_arrow_style).unwrap();
+ }
+ if self.streamplot_density > 0.0 {
+ write!(&mut opt, ",density={}", self.streamplot_density).unwrap();
+ }
+ if self.streamplot_extra != "" {
+ write!(&mut opt, ",{}", self.streamplot_extra).unwrap();
+ }
+ opt
+ }
+
+ /// Returns options for quiver
+ fn options_quiver(&self) -> String {
+ let mut opt = String::new();
+ if self.color != "" {
+ write!(&mut opt, ",color='{}'", self.color).unwrap();
+ }
+ if self.quiver_scale > 0.0 {
+ write!(&mut opt, ",scale={}", self.quiver_scale).unwrap();
+ }
+ if self.quiver_pivot != "" {
+ write!(&mut opt, ",pivot='{}'", self.quiver_pivot).unwrap();
+ }
+ if self.quiver_extra != "" {
+ write!(&mut opt, ",{}", self.quiver_extra).unwrap();
+ }
+ opt
+ }
+}
+
+impl GraphMaker for Stream {
+ fn get_buffer<'a>(&'a self) -> &'a String {
+ &self.buffer
+ }
+ fn clear_buffer(&mut self) {
+ self.buffer.clear();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#[cfg(test)]
+mod tests {}
diff --git a/tests/test_stream.rs b/tests/test_stream.rs
new file mode 100644
index 0000000..0b7944d
--- /dev/null
+++ b/tests/test_stream.rs
@@ -0,0 +1,56 @@
+use plotpy::{generate2d, Plot, StrError, Stream};
+use std::fs::File;
+use std::io::{BufRead, BufReader};
+use std::path::Path;
+
+const OUT_DIR: &str = "/tmp/plotpy/integ_tests";
+
+#[test]
+fn test_stream_arrows_1() -> Result<(), StrError> {
+ // object and options
+ let mut stream = Stream::new();
+ let mut quiver = Stream::new();
+
+ // 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);
+ 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;
+ }
+ }
+
+ // draw arrows
+ stream
+ .set_color("#dfa629ff")
+ .set_streamline_linewidth(0.75)
+ .set_streamplot_arrow_style("fancy")
+ .set_streamplot_density(0.8)
+ .set_streamplot_extra("broken_streamlines=False")
+ .draw(&xx, &yy, &dx, &dy);
+ quiver
+ .set_color("#4752c7ff")
+ .set_quiver_inv_scale(15.0)
+ .set_quiver_pivot("mid")
+ .draw_arrows(&xx, &yy, &dx, &dy);
+
+ // 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_1.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 > 8650 && n < 8730);
+ Ok(())
+}