From e83e3789d539ea90d27fbb97b80fcca2b01b174f Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 10:38:14 +1000 Subject: [PATCH 01/20] Add Architecture section --- README.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/README.md b/README.md index b1ed957..cf8a11c 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,9 @@ - [InsetAxes](#insetaxes) - [Surface](#surface) - [Text](#text) +- [Architecture](#architecture) + - [Chaining pattern (builder style)](#chaining-pattern-builder-style) + - [Consistent conventions across all files](#consistent-conventions-across-all-files) ## Introduction @@ -506,3 +509,51 @@ fn main() -> Result<(), StrError> { ``` ![text](https://raw.githubusercontent.com/cpmech/plotpy/main/figures/doc_text.svg) + +--- + +## Architecture + +(Generated by [DeepSeek](https://www.deepseek.com/)) + +**Core idea**: Generates Python 3 scripts as strings from Rust, then executes them via `python3`. Not a direct API wrapper — it's a code generator. + +- **25 source files** in `src/`, each a standalone module +- Each "graph entity" struct (`Curve`, `Barplot`, `Boxplot`, `Contour`, `Surface`, `Canvas`, `Histogram`, `Text`, etc.) implements `GraphMaker` trait (`get_buffer()` + `clear_buffer()`) +- `Plot` is the central coordinator — collects buffers via `add(&entity)`, prepends a Python header, appends `plt.savefig()`, writes `.py` file, executes it +- Only one dependency: `num-traits = "0.2"` (for generic `Num` bound) +- Two data abstraction traits: `AsVector` (for 1D data) and `AsMatrix` (for 2D data) + +### Chaining pattern (builder style) + +The entire library follows `something.method1().method2().method3()` pervasively. + +**Graph entities** — setters return `&mut Self`: +```rust +curve.set_label("logistic") + .set_line_color("#5f9cd8") + .set_line_style("-") + .set_line_width(5.0); +curve.draw(&x, &y); +``` +Note: `draw()` methods don't return `&mut Self` (they finalize by writing Python code). But `points_begin()`/`points_add()`/`points_end()` do chain. + +**Plot** — everything returns `&mut Self`: +```rust +plot.set_subplot(2, 2, 1) + .set_title("first") + .add(&curve1) + .grid_labels_legend("x", "y") + .set_equal_axes(true); +``` + +### Consistent conventions across all files + +- `new()` → defaults (empty strings, `0.0` sentinels) +- `set_*()` → returns `&mut Self` +- `options()` → private method builds CSV-style parameter string +- `draw()` → writes Python to `buffer` using `write!` macro (all `.unwrap()` since `String` writes are infallible) +- `GraphMaker` impl → exposes the buffer +- Inline `#[cfg(test)] mod tests` in every file + integration tests under `tests/` +- `max_width = 120` in `rustfmt.toml` +- Error type: `pub type StrError = &'static str;` From 735dfe28e870e12fd3910e258c7d72bd23894a38 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 10:45:42 +1000 Subject: [PATCH 02/20] Improve doc of auxiliary functions --- .vscode/settings.json | 4 ++ README.md | 4 +- src/auxiliary.rs | 89 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 83 insertions(+), 14 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 38d51ea..d864240 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -66,12 +66,14 @@ "linestyles", "LINETO", "linewidths", + "linspace", "markeredgecolor", "markeredgewidth", "markerfacecolor", "markersize", "markevery", "Matplotlib", + "meshgrid", "MOVETO", "mplot", "nord", @@ -88,6 +90,8 @@ "savefig", "showfliers", "stepfilled", + "superquadric", + "superquadrics", "suptitle", "textcolor", "TICKDOWN", diff --git a/README.md b/README.md index cf8a11c..a0e77c4 100644 --- a/README.md +++ b/README.md @@ -529,7 +529,7 @@ fn main() -> Result<(), StrError> { The entire library follows `something.method1().method2().method3()` pervasively. **Graph entities** — setters return `&mut Self`: -```rust +```text curve.set_label("logistic") .set_line_color("#5f9cd8") .set_line_style("-") @@ -539,7 +539,7 @@ curve.draw(&x, &y); Note: `draw()` methods don't return `&mut Self` (they finalize by writing Python code). But `points_begin()`/`points_add()`/`points_end()` do chain. **Plot** — everything returns `&mut Self`: -```rust +```text plot.set_subplot(2, 2, 1) .set_title("first") .add(&curve1) diff --git a/src/auxiliary.rs b/src/auxiliary.rs index 8d875c7..3fe06a5 100644 --- a/src/auxiliary.rs +++ b/src/auxiliary.rs @@ -23,29 +23,59 @@ pub fn sign(x: f64) -> f64 { } } -/// Implements the superquadric function involving sin(x) +/// Implements the superquadric auxiliary function involving sin(x) /// /// ```text /// suq_sin(x;k) = sign(sin(x)) · |sin(x)|ᵏ /// ``` /// -/// `suq_sin(x;k)` is the `f(ω;m)` function from +/// This is the angular shaping function for [superquadrics](https://en.wikipedia.org/wiki/Superquadrics). +/// When `k = 2` the result is the standard parametric form of a sphere/ellipsoid. +/// Values `k < 1` produce a "pinched" shape; `k > 2` produces a "squared" shape. +/// +/// `suq_sin(x;k)` corresponds to `f(ω;m)` in the superquadric literature. +/// +/// See also: [`suq_cos`] pub fn suq_sin(x: f64, k: f64) -> f64 { sign(f64::sin(x)) * f64::powf(f64::abs(f64::sin(x)), k) } -/// Implements the superquadric auxiliary involving cos(x) +/// Implements the superquadric auxiliary function involving cos(x) /// /// ```text /// suq_cos(x;k) = sign(cos(x)) · |cos(x)|ᵏ /// ``` /// -/// `suq_cos(x;k)` is the `g(ω;m)` function from +/// This is the angular shaping function for [superquadrics](https://en.wikipedia.org/wiki/Superquadrics). +/// When `k = 2` the result is the standard parametric form of a sphere/ellipsoid. +/// Values `k < 1` produce a "pinched" shape; `k > 2` produces a "squared" shape. +/// +/// `suq_cos(x;k)` corresponds to `g(ω;m)` in the superquadric literature. +/// +/// See also: [`suq_sin`] pub fn suq_cos(x: f64, k: f64) -> f64 { sign(f64::cos(x)) * f64::powf(f64::abs(f64::cos(x)), k) } /// Returns evenly spaced numbers over a specified closed interval +/// +/// Analogous to [numpy.linspace](https://numpy.org/doc/stable/reference/generated/numpy.linspace.html). +/// Both `start` and `stop` are included in the output (closed interval). +/// +/// # Examples +/// +/// ``` +/// use plotpy::linspace; +/// +/// assert_eq!(linspace(0.0, 1.0, 3), vec![0.0, 0.5, 1.0]); +/// assert_eq!(linspace(0.0, 1.0, 5), vec![0.0, 0.25, 0.5, 0.75, 1.0]); +/// ``` +/// +/// # Edge cases +/// +/// - `count == 0` returns an empty vector +/// - `count == 1` returns `[start]` +/// - `count == 2` returns `[start, stop]` pub fn linspace(start: f64, stop: f64, count: usize) -> Vec { if count == 0 { return Vec::new(); @@ -68,18 +98,34 @@ pub fn linspace(start: f64, stop: f64, count: usize) -> Vec { res } -/// Generates 2d points (meshgrid) +/// Generates 2d meshgrid points +/// +/// This is analogous to [numpy.meshgrid](https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html) +/// with `indexing='ij'`. Produces two (`ny` × `nx`) matrices where each row has the same `y` +/// value and each column has the same `x` value. /// /// # Input /// /// * `xmin`, `xmax` -- range along x /// * `ymin`, `ymax` -- range along y -/// * `nx` -- is the number of points along x (must be `>= 2`) -/// * `ny` -- is the number of points along y (must be `>= 2`) +/// * `nx` -- number of points along x (must be `>= 2`) +/// * `ny` -- number of points along y (must be `>= 2`) /// /// # Output /// -/// * `x`, `y` -- (`ny` by `nx`) 2D arrays +/// * `x`, `y` -- (`ny` by `nx`) 2D arrays such that `x[i][j] = xmin + j·dx` and `y[i][j] = ymin + i·dy` +/// +/// # Example +/// +/// ``` +/// use plotpy::generate2d; +/// +/// let (x, y) = generate2d(-1.0, 1.0, -3.0, 3.0, 2, 3); +/// assert_eq!(x, vec![vec![-1.0, 1.0], vec![-1.0, 1.0], vec![-1.0, 1.0]]); +/// assert_eq!(y, vec![vec![-3.0, -3.0], vec![0.0, 0.0], vec![3.0, 3.0]]); +/// ``` +/// +/// See also: [`generate3d`] pub fn generate2d(xmin: f64, xmax: f64, ymin: f64, ymax: f64, nx: usize, ny: usize) -> (Vec>, Vec>) { let mut x = vec![vec![0.0; nx]; ny]; let mut y = vec![vec![0.0; nx]; ny]; @@ -107,19 +153,38 @@ pub fn generate2d(xmin: f64, xmax: f64, ymin: f64, ymax: f64, nx: usize, ny: usi (x, y) } -/// Generates 3d points (function over meshgrid) +/// Generates 3d points by evaluating a function over a 2d meshgrid +/// +/// Creates the same `(x, y)` grid as [`generate2d`], then evaluates `calc_z(x, y)` at each +/// grid point to produce the `z` matrix. This is the typical input for [`Surface::draw`](crate::Surface::draw) +/// and [`Contour::draw`](crate::Contour::draw). /// /// # Input /// /// * `xmin`, `xmax` -- range along x /// * `ymin`, `ymax` -- range along y -/// * `nx` -- is the number of points along x (must be `>= 2`) -/// * `ny` -- is the number of points along y (must be `>= 2`) -/// * `calc_z` -- is a function of (xij, yij) that calculates zij +/// * `nx` -- number of points along x (must be `>= 2`) +/// * `ny` -- number of points along y (must be `>= 2`) +/// * `calc_z` -- function `f(x, y)` that returns `z` at each grid point /// /// # Output /// /// * `x`, `y`, `z` -- (`ny` by `nx`) 2D arrays +/// +/// # Example +/// +/// ``` +/// use plotpy::generate3d; +/// +/// let (x, y, z) = generate3d(-1.0, 1.0, -1.0, 1.0, 3, 3, |x, y| x * x + y * y); +/// assert_eq!(z, vec![ +/// vec![2.0, 1.0, 2.0], +/// vec![1.0, 0.0, 1.0], +/// vec![2.0, 1.0, 2.0], +/// ]); +/// ``` +/// +/// See also: [`generate2d`] pub fn generate3d( xmin: f64, xmax: f64, From e78c3a5662c08bc4f2af94f31e4d9be44f942a0d Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 11:04:11 +1000 Subject: [PATCH 03/20] Improve barplot doc --- src/barplot.rs | 45 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/src/barplot.rs b/src/barplot.rs index 08ff2b8..8bf96f2 100644 --- a/src/barplot.rs +++ b/src/barplot.rs @@ -8,11 +8,10 @@ use std::fmt::Write; /// /// # Examples /// -/// ## Basic bar plot +/// ## Basic bar plot (numeric x-axis) /// /// ``` /// use plotpy::{Barplot, Plot, StrError}; -/// use std::collections::HashMap; /// /// fn main() -> Result<(), StrError> { /// // data @@ -138,7 +137,14 @@ impl Barplot { } } - /// Draws the bar plot + /// Draws the bar plot with numeric x-axis + /// + /// # Input + /// + /// * `x` -- bar positions on the x-axis (or values when horizontal) + /// * `y` -- bar heights (or lengths when horizontal) + /// + /// See also: [`draw_with_str`](Self::draw_with_str) for string-labeled categories pub fn draw<'a, T, U>(&mut self, x: &'a T, y: &'a T) where T: AsVector<'a, U>, @@ -166,7 +172,14 @@ impl Barplot { } } - /// Draws the bar plot with strings + /// Draws the bar plot with string labels on the x-axis (categorical data) + /// + /// # Input + /// + /// * `x` -- string labels for each bar (e.g., `["Apple", "Banana", "Orange"]`) + /// * `y` -- bar heights (or lengths when horizontal) + /// + /// See also: [`draw`](Self::draw) for numeric x-axis pub fn draw_with_str<'a, T, U>(&mut self, x: &[&str], y: &'a T) where T: AsVector<'a, U>, @@ -194,25 +207,32 @@ impl Barplot { } } - /// Sets the name of this bar in the legend + /// Sets the name of this bar series in the legend + /// + /// When multiple bar groups are drawn (e.g., stacked or grouped bars), + /// each label identifies that group in the legend produced by [`Plot::legend`](crate::Plot::legend). pub fn set_label(&mut self, label: &str) -> &mut Self { self.label = String::from(label); self } - /// Sets the colors for each bar + /// Sets the colors for each bar (one color per bar, matched by index) pub fn set_colors(&mut self, colors: &[&str]) -> &mut Self { self.colors = colors.iter().map(|color| color.to_string()).collect(); self } - /// Sets the width of the bars + /// Sets the width of each bar (relative to the default unit spacing) pub fn set_width(&mut self, width: f64) -> &mut Self { self.width = width; self } - /// Sets the vertical offset to stack bars + /// Sets the vertical offset of each bar (bottom coordinate) + /// + /// Use this to create **stacked bar charts**. Pass a slice with the same + /// number of elements as bars, where each element is the cumulative + /// height of bars drawn below this one. pub fn set_bottom(&mut self, bottom: &[f64]) -> &mut Self { self.bottom = Vec::from(bottom); self @@ -235,12 +255,19 @@ impl Barplot { } /// Enables drawing horizontal bars + /// + /// When `true`, the x-axis shows bar lengths and the y-axis shows categories. + /// Consider calling [`Plot::set_inv_y`](crate::Plot::set_inv_y) when using + /// string labels so the first label appears at the top. pub fn set_horizontal(&mut self, flag: bool) -> &mut Self { self.horizontal = flag; self } - /// Enables error indicators + /// Enables error indicators (one error value per bar) + /// + /// When used with [`set_horizontal(true)`](Self::set_horizontal), errors + /// apply to the x-axis instead of the y-axis. pub fn set_errors(&mut self, errors: &[f64]) -> &mut Self { self.errors = errors.to_vec(); self From f795b20f240049ab0643ad20fb4dafa5155b4c98 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 11:06:31 +1000 Subject: [PATCH 04/20] Improve boxplot doc --- src/boxplot.rs | 62 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/src/boxplot.rs b/src/boxplot.rs index 8af2b82..c7a7e38 100644 --- a/src/boxplot.rs +++ b/src/boxplot.rs @@ -4,6 +4,13 @@ use std::fmt::Write; /// Draw a box and whisker plot /// +/// Each box shows the median, quartiles (Q1/Q3), and whiskers (default: 1.5 × IQR). +/// Outlier points (fliers) are drawn beyond the whiskers. +/// +/// Two data formats are supported: +/// - **Nested list** (via [`draw`](Self::draw)): one box per sub-array (variable group sizes) +/// - **2D matrix** (via [`draw_mat`](Self::draw_mat)): one box per column (equal group sizes) +/// /// [See Matplotlib's documentation](https://matplotlib.org/3.6.3/api/_as_gen/matplotlib.pyplot.boxplot.html) /// /// # Examples @@ -120,12 +127,15 @@ impl Boxplot { } } - /// Draws the box plot given a nested list + /// Draws the box plot given a nested list of values + /// + /// Each inner `Vec` produces one box. Groups can have different sizes. /// /// # Input /// - /// * `data` -- Is a sequence of 1D arrays such that a boxplot is drawn for each array in the sequence. - /// [From Matplotlib](https://matplotlib.org/3.6.3/api/_as_gen/matplotlib.pyplot.boxplot.html) + /// * `data` -- a sequence of 1D arrays; one box is drawn per sub-array + /// + /// See also: [`draw_mat`](Self::draw_mat) for matrix-oriented data pub fn draw(&mut self, data: &Vec>) where T: std::fmt::Display + Num, @@ -138,12 +148,14 @@ impl Boxplot { write!(&mut self.buffer, "p=plt.boxplot(x{})\n", &opt).unwrap(); } - /// Draws the box plot given a 2D array (matrix) + /// Draws the box plot given a 2D matrix of values + /// + /// Each **column** of the matrix produces one box. All groups must have + /// the same number of rows (unlike [`draw`](Self::draw) which allows variable sizes). /// /// # Input /// - /// * `data` -- Is a 2D array (matrix) such that a boxplot is drawn for each column in the matrix. - /// [From Matplotlib](https://matplotlib.org/3.6.3/api/_as_gen/matplotlib.pyplot.boxplot.html) + /// * `data` -- a 2D matrix (rows = observations, columns = groups) pub fn draw_mat<'a, T, U>(&mut self, data: &'a T) where T: AsMatrix<'a, U>, @@ -157,13 +169,18 @@ impl Boxplot { write!(&mut self.buffer, "p=plt.boxplot(x{})\n", &opt).unwrap(); } - /// Sets the symbol for the fliers + /// Sets the [marker symbol](https://matplotlib.org/stable/api/markers_api.html) for outlier points (fliers) + /// + /// Example: `"b+"` for blue crosses, `"rx"` for red x-marks. pub fn set_symbol(&mut self, symbol: &str) -> &mut Self { self.symbol = symbol.to_string(); self } - /// Enables drawing horizontal boxes + /// Enables drawing horizontal boxes instead of vertical + /// + /// When `true`, the boxplot is rotated 90° clockwise so categories + /// appear on the y-axis. pub fn set_horizontal(&mut self, flag: bool) -> &mut Self { self.horizontal = flag; self @@ -179,31 +196,41 @@ impl Boxplot { self } - /// Sets the positions of the boxes + /// Overrides the default x-axis positions of the boxes + /// + /// By default boxes are placed at sequential integer positions (1, 2, 3, …). + /// Use this to spread or group boxes at custom locations. pub fn set_positions(&mut self, positions: &[f64]) -> &mut Self { self.positions = positions.to_vec(); self } - /// Sets the width of the boxes + /// Sets the width of each box (default: 0.5) pub fn set_width(&mut self, width: f64) -> &mut Self { self.width = Some(width); self } - /// Disables the fliers + /// Hides outlier points (fliers) when `true` + /// + /// Outliers are data points beyond 1.5 × IQR from the quartiles. pub fn set_no_fliers(&mut self, flag: bool) -> &mut Self { self.no_fliers = flag; self } - /// Enables the use of Patch artist to draw boxes instead of Line2D artist + /// Enables Patch artist drawing so boxes can be filled with color + /// + /// By default Matplotlib uses Line2D to draw boxes. Setting this to `true` + /// allows [`set_boxprops`](Self::set_boxprops) to fill boxes with color. pub fn set_patch_artist(&mut self, flag: bool) -> &mut Self { self.patch_artist = flag; self } - /// Set the median properties. + /// Sets the median line properties as a Python dict string + /// + /// Example: `"{'color': 'red', 'linewidth': 2}"` /// /// [See Matplotlib's documentation](https://matplotlib.org/3.6.3/api/_as_gen/matplotlib.pyplot.boxplot.html) pub fn set_medianprops(&mut self, props: &str) -> &mut Self { @@ -211,13 +238,18 @@ impl Boxplot { self } - /// Set the properties of the box + /// Sets the box body properties as a Python dict string + /// + /// Requires [`set_patch_artist(true)`](Self::set_patch_artist). + /// Example: `"{'facecolor': 'lightblue', 'edgecolor': 'black'}"` pub fn set_boxprops(&mut self, props: &str) -> &mut Self { self.box_props = props.to_string(); self } - /// Set the properties of the whisker + /// Sets the whisker line properties as a Python dict string + /// + /// Example: `"{'linestyle': '--', 'color': 'gray'}"` pub fn set_whiskerprops(&mut self, props: &str) -> &mut Self { self.whisker_props = props.to_string(); self From 075f26372b5c041e3607c118601c174e026e23de Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 11:08:04 +1000 Subject: [PATCH 05/20] Improve canvas doc --- src/canvas.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/src/canvas.rs b/src/canvas.rs index dbe22bf..d0c1d92 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -211,6 +211,12 @@ impl Canvas { } /// Draws arc (2D only) + /// + /// # Input + /// + /// * `xc`, `yc` -- center coordinates + /// * `r` -- radius + /// * `ini_angle`, `fin_angle` -- arc angles in degrees (counter-clockwise) pub fn draw_arc(&mut self, xc: T, yc: T, r: T, ini_angle: T, fin_angle: T) where T: std::fmt::Display + Num, @@ -226,6 +232,13 @@ impl Canvas { } /// Draws arrow (2D only) + /// + /// # Input + /// + /// * `xi`, `yi` -- start point + /// * `xf`, `yf` -- end point + /// + /// Use [`set_arrow_style`](Self::set_arrow_style) and [`set_arrow_scale`](Self::set_arrow_scale) to configure. pub fn draw_arrow(&mut self, xi: T, yi: T, xf: T, yf: T) where T: std::fmt::Display + Num, @@ -245,6 +258,11 @@ impl Canvas { } /// Draws circle (2D only) + /// + /// # Input + /// + /// * `xc`, `yc` -- center coordinates + /// * `r` -- radius pub fn draw_circle(&mut self, xc: T, yc: T, r: T) where T: std::fmt::Display + Num, @@ -484,7 +502,11 @@ impl Canvas { self } - /// Draws polyline (2D or 3D) + /// Draws polyline (2D or 3D, auto-detected from point dimensions) + /// + /// If points are `2D` (`ndim == 2`), uses 2D path patches (supporting + /// `face_color` and `edge_color` fills). If `3D` (`ndim == 3`), draws as + /// a 3D line plot. pub fn draw_polyline<'a, T, U>(&mut self, points: &'a T, closed: bool) where T: AsMatrix<'a, U>, @@ -538,7 +560,13 @@ impl Canvas { } } - /// Draws a rectangle + /// Draws a rectangle (2D only) + /// + /// # Input + /// + /// * `x`, `y` -- bottom-left corner coordinates + /// * `width` -- rectangle width + /// * `height` -- rectangle height pub fn draw_rectangle(&mut self, x: T, y: T, width: T, height: T) -> &mut Self where T: std::fmt::Display + Num, @@ -554,7 +582,11 @@ impl Canvas { self } - /// Draws a text in a 2D graph + /// Draws text at (x, y) in a 2D graph using the primary text style + /// + /// Configure via `set_text_*` methods (color, alignment, fontsize, rotation). + /// + /// See also: [`draw_alt_text`](Self::draw_alt_text) for the alternative text style pub fn draw_text(&mut self, x: T, y: T, label: &str) -> &mut Self where T: std::fmt::Display + Num, @@ -563,7 +595,13 @@ impl Canvas { self } - /// Draws an alternative text in a 2D graph + /// Draws text at (x, y) in a 2D graph using the alternative text style + /// + /// The alternative style has distinct default values (red color, smaller font, + /// rotated 45°) designed for point/cell ID labels in grid drawings. + /// Configure via `set_alt_text_*` methods. + /// + /// See also: [`draw_text`](Self::draw_text) for the primary text style pub fn draw_alt_text(&mut self, x: T, y: T, label: &str) -> &mut Self where T: std::fmt::Display + Num, @@ -771,13 +809,13 @@ impl Canvas { Ok(()) } - /// Sets the edge color (shared among features) + /// Sets the edge color for the border of shapes (shared among all draw methods) pub fn set_edge_color(&mut self, color: &str) -> &mut Self { self.edge_color = String::from(color); self } - /// Sets the face color (shared among features) + /// Sets the fill color for the interior of shapes (shared among all draw methods) pub fn set_face_color(&mut self, color: &str) -> &mut Self { self.face_color = String::from(color); self @@ -789,7 +827,7 @@ impl Canvas { self } - /// Sets the line width of edge (shared among features) + /// Sets the line style for edges (shared among features) /// /// Options: /// From 0817c33e9a5a78ccdff00bb3f540042536e7e206 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 11:10:13 +1000 Subject: [PATCH 06/20] Improve contour doc --- .vscode/settings.json | 3 +++ src/contour.rs | 52 +++++++++++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index d864240..9ee5e61 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -32,6 +32,8 @@ "clabel", "CLOSEPOLY", "cmap", + "colorbar", + "colormap", "COLORMAPS", "columnspacing", "contourf", @@ -54,6 +56,7 @@ "horizontalalignment", "hspace", "imshow", + "integ", "joinstyle", "kwargs", "labelcolor", diff --git a/src/contour.rs b/src/contour.rs index d803fe6..44d097e 100644 --- a/src/contour.rs +++ b/src/contour.rs @@ -5,6 +5,14 @@ use std::fmt::Write; /// Generates a contour plot /// +/// By default, draws a **filled contour** with a **line contour** overlay, +/// a **colorbar**, and optional **selected-level** highlighting. +/// Individual layers can be disabled via the `set_no_*` methods. +/// +/// Two drawing modes are supported: +/// - **Grid data** via [`draw`](Self::draw) -- uses matrices `(x, y, z)` on a regular grid +/// - **Triangulated data** via [`draw_tri`](Self::draw_tri) -- uses scattered `(x, y, z)` points with a connectivity matrix +/// /// [See Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contour.html) /// /// # Example @@ -241,18 +249,22 @@ impl Contour { self } - /// Sets the colormap index + /// Sets a colormap by index from a built-in list /// - /// Options: + /// Indices wrap around: /// - /// * 0 -- bwr - /// * 1 -- RdBu - /// * 2 -- hsv - /// * 3 -- jet - /// * 4 -- terrain - /// * 5 -- pink - /// * 6 -- Greys - /// * `>`6 -- starts over from 0 + /// | Index | Colormap | + /// |-------|----------| + /// | 0 | bwr | + /// | 1 | RdBu | + /// | 2 | hsv | + /// | 3 | jet | + /// | 4 | terrain | + /// | 5 | pink | + /// | 6 | Greys | + /// + /// Calling this clears any colors set via [`set_colors`](Self::set_colors). + /// See also: [`set_colormap_name`](Self::set_colormap_name) 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(); @@ -260,13 +272,13 @@ impl Contour { self } - /// Sets the colormap name - /// - /// Colormap names: + /// Sets the colormap by name (e.g., `"terrain"`, `"jet"`, `"viridis"`) /// - /// * see + /// See the [Matplotlib colormap reference](https://matplotlib.org/stable/tutorials/colors/colormaps.html) + /// for all available options. /// - /// Will use `colormap_index` instead if `colormap_name` is empty. + /// Calling this clears any colors set via [`set_colors`](Self::set_colors). + /// See also: [`set_colormap_index`](Self::set_colormap_index) pub fn set_colormap_name(&mut self, name: &str) -> &mut Self { self.colormap_name = String::from(name); self.colors = Vec::new(); @@ -327,11 +339,11 @@ impl Contour { self } - /// Sets the colorbar location + /// Sets the colorbar location (default: Matplotlib's "best" placement) /// - /// Options: 'right', 'left', 'top', 'bottom' + /// Options: `"right"`, `"left"`, `"top"`, `"bottom"` /// - /// See: + /// See also: [`set_colorbar_axes`](Self::set_colorbar_axes) for fine-grained control pub fn set_colorbar_location(&mut self, location: &str) -> &mut Self { self.colorbar_location = location.to_string(); self @@ -345,7 +357,9 @@ impl Contour { self } - /// Sets the number format for the labels in the colorbar (cb) + /// Sets the number format for the colorbar tick labels + /// + /// Example: `"%.2f"` for two decimal places, `"%.4e"` for scientific notation pub fn set_number_format_cb(&mut self, format: &str) -> &mut Self { self.number_format_cb = String::from(format); self From 7924adea73ff9e9c07dca7be9222ac6f041a44d1 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 11:12:35 +1000 Subject: [PATCH 07/20] Improve curve doc --- .vscode/settings.json | 1 + src/curve.rs | 64 +++++++++++++++++++++++++++++-------------- 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 9ee5e61..2c0d869 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -75,6 +75,7 @@ "markerfacecolor", "markersize", "markevery", + "Mathematica", "Matplotlib", "meshgrid", "MOVETO", diff --git a/src/curve.rs b/src/curve.rs index 92f78b2..6429ecf 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -21,12 +21,11 @@ pub enum RayEndpoint { /// Generates a curve (aka line-plot) given two arrays (x,y) /// -/// [See Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.plot.html) -/// -/// # Notes +/// Supports 2D and 3D line plots, scatter plots (set `line_style = "None"`), +/// infinite rays (`draw_ray`), twin-x dual-axis plots (`draw_with_twin_x`), +/// and point-by-point construction (`points_begin`/`points_add`/`points_end`). /// -/// * This struct corresponds to the **plot** function of Matplotlib. -/// * You may plot a Scatter plot by setting line_style = "None" +/// [See Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.plot.html) /// /// # Examples /// @@ -276,12 +275,14 @@ impl Curve { self } - /// Draws curve + /// Draws a 2D line plot /// /// # Input /// - /// * `x` - abscissa values - /// * `y` - ordinate values + /// * `x` -- x-axis (abscissa) values + /// * `y` -- y-axis (ordinate) values + /// + /// See also: [`draw_3d`](Self::draw_3d) for 3D curves pub fn draw<'a, T, U>(&mut self, x: &'a T, y: &'a T) where T: AsVector<'a, U>, @@ -293,9 +294,13 @@ impl Curve { write!(&mut self.buffer, "plt.plot(x,y{})\n", &opt).unwrap(); } - /// Draws curve on a previously drawn figure with the same x + /// Draws curve on a secondary (right-hand) y-axis sharing the same x-axis + /// + /// Must be called after a regular [`draw`](Self::draw) that sets up the x-axis. + /// Configure the twin-x label and color via [`Plot::set_label_y_twinx`](crate::Plot::set_label_y_twinx) + /// and [`Plot::set_label_y_twinx_color`](crate::Plot::set_label_y_twinx_color). /// - /// * `y` - ordinate values on the right-hand side + /// * `y` -- ordinate values for the right-hand side axis pub fn draw_with_twin_x<'a, T, U>(&mut self, y: &'a T) where T: AsVector<'a, U>, @@ -316,11 +321,15 @@ impl Curve { /// Draws curve in 3D plot /// + /// Requires [`Plot::set_subplot_3d`](crate::Plot::set_subplot_3d) to be called first. + /// /// # Input /// - /// * `x` - x values - /// * `y` - y values - /// * `z` - z values + /// * `x` -- x-axis values + /// * `y` -- y-axis values + /// * `z` -- z-axis values + /// + /// See also: [`draw`](Self::draw) for 2D curves pub fn draw_3d<'a, T, U>(&mut self, x: &'a T, y: &'a T, z: &'a T) where T: AsVector<'a, U>, @@ -339,7 +348,10 @@ impl Curve { self } - /// Sets the opacity of lines (0, 1]. A<1e-14 => A=1.0 + /// Sets the opacity (transparency) of lines + /// + /// Range `(0, 1]` where `0.0` = fully transparent and `1.0` = fully opaque. + /// Values below `1e-14` are treated as `1.0` (opaque). pub fn set_line_alpha(&mut self, alpha: f64) -> &mut Self { self.line_alpha = alpha; self @@ -351,10 +363,13 @@ impl Curve { self } - /// Draws a ray (an infinite line) + /// Draws a ray (an infinite line through two points, or by slope) /// - /// * For horizontal rays, only `ya` is used - /// * For vertical rays, only `xa` is used + /// Use [`RayEndpoint`] to specify the ray type: + /// - `Coords(xb, yb)` -- ray through `(xa, ya)` and `(xb, yb)` + /// - `Slope(m)` -- ray through `(xa, ya)` with given slope + /// - `Horizontal` -- horizontal line at `ya` (uses only `ya`) + /// - `Vertical` -- vertical line at `xa` (uses only `xa`) pub fn draw_ray(&mut self, xa: f64, ya: f64, endpoint: RayEndpoint) { let opt = self.options(); match endpoint { @@ -398,13 +413,19 @@ impl Curve { self } - /// Sets the increment of data points to use when drawing markers + /// Sets the spacing of marker symbols along the curve + /// + /// For example, `every = 5` places a marker on every 5th data point. + /// Set to `0` to disable markers. pub fn set_marker_every(&mut self, every: usize) -> &mut Self { self.marker_every = every; self } - /// Sets the option to draw a void marker (draw edge only) + /// Enables hollow (edge-only) markers with a transparent face + /// + /// When `true`, the marker interior is not filled — only the edge is drawn, + /// using the color set via [`set_marker_line_color`](Self::set_marker_line_color). pub fn set_marker_void(&mut self, flag: bool) -> &mut Self { self.marker_void = flag; self @@ -439,7 +460,10 @@ impl Curve { self } - /// Sets the flag to stop clipping features within margins + /// Disables clipping of the curve at the plot margins + /// + /// By default, curves are clipped at the axes boundary. Set to `true` to allow + /// the curve to extend into the margin area (useful for markers near the edges). pub fn set_stop_clip(&mut self, flag: bool) -> &mut Self { self.stop_clip = flag; self From bf3b2b1debcaa049ef20108c9efc1d2a87f7e2d3 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 11:14:26 +1000 Subject: [PATCH 08/20] Improve fill_between doc --- src/fill_between.rs | 60 +++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/src/fill_between.rs b/src/fill_between.rs index aec305d..71be219 100644 --- a/src/fill_between.rs +++ b/src/fill_between.rs @@ -4,6 +4,11 @@ use std::fmt::Write; /// Fills the area between two curves /// +/// When `y2` is `None`, fills the area between `y1` and the x-axis (`y = 0`). +/// +/// **Important:** The generated Python script uses `y1` and `y2` as variable names. +/// Any `where_condition` (e.g., for two-color filling) must reference these names. +/// /// # Examples /// /// ``` @@ -52,12 +57,19 @@ impl FillBetween { } } - /// Draws the filled area between two curves + /// Draws the filled area between two curves (or between a curve and the x-axis) + /// + /// # Input /// - /// * `x` - x values - /// * `y1` - y values of the first curve - /// * `y2` - optional y values of the second curve. If None, fills area between y1 and x-axis + /// * `x` -- shared x-axis values + /// * `y1` -- y values of the first curve (variable name: `y1`) + /// * `y2` -- optional y values of the second curve (variable name: `y2`). + /// If `None`, fills the area between `y1` and the x-axis (`y = 0`). /// + /// # Note + /// + /// For two-color filling (above/below a threshold), use [`set_where`](Self::set_where) + /// with a condition referencing `y1` and `y2`. pub fn draw<'a, T, U>(&mut self, x: &'a T, y1: &'a T, y2: Option<&'a T>) where T: AsVector<'a, U>, @@ -77,51 +89,51 @@ impl FillBetween { } } - /// Sets the condition to select the area to be filled. + /// Sets the condition that selects which region to fill /// - /// For example: "y2>=y1" or "y2<=y1" + /// **The condition must use `y1` and `y2` as variable names.** /// - /// **WARNING:** `condition` must use `y1` and `y2` as variable names for the two curves. + /// Examples: + /// - `"y1>=0.5"` -- fill where the curve is above 0.5 on the y-axis + /// - `"y2>=y1"` -- fill where the second curve is above the first pub fn set_where(&mut self, condition: &str) -> &mut Self { self.where_condition = condition.to_string(); self } - /// Sets the face color of the filled area. + /// Sets the fill color of the shaded area pub fn set_facecolor(&mut self, color: &str) -> &mut Self { self.facecolor = color.to_string(); self } - /// Calculates the actual intersection point and extend the filled region up to this point. + /// When `true`, calculates the actual intersection point of crossing curves + /// and extends the filled region up to that point for a clean edge /// - /// From : + /// This only matters when using [`set_where`](Self::set_where) with crossing curves. + /// Default is `false`. /// - /// "This option is only relevant if where is used and the two curves are crossing each other. Semantically, - /// `where` is often used for y1 > y2 or similar. By default, the nodes of the polygon defining the filled - /// region will only be placed at the positions in the x array. Such a polygon cannot describe the above - /// semantics close to the intersection. The x-sections containing the intersection are simply clipped." + /// From the [Matplotlib docs](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.fill_between.html): /// - /// Default is false. + /// "Semantically, `where` is often used for `y1 > y2` or similar. By default, + /// the nodes of the polygon defining the filled region will only be placed at the + /// positions in the x array. Such a polygon cannot describe the above semantics + /// close to the intersection. The x-sections containing the intersection are + /// simply clipped." pub fn set_interpolate(&mut self, interpolate: bool) -> &mut Self { self.interpolate = interpolate; self } - /// Fills the area between two curves + /// Sets extra matplotlib commands (comma separated) for `fill_between` /// - /// **WARNING:** `where_condition` must use `y1` and `y2` as variable names for the two curves. - /// For example: + /// **Important:** The extra commands must be comma separated. For example: /// /// ```text - /// curve.fill_between(x, y1, y2, "y2>=y1", "#ffaabb", true, ""); - /// curve.fill_between(x, y1, y2, "y2>=y1", "#ffaabb", true, ""); - /// curve.fill_between(x, y1, y2b, "y2<=y1", "#c1e3ff", true, ""); + /// alpha=0.5,color='#ffaabb' /// ``` /// - /// **Note:** This method does not use the options of the Curve object. - /// - /// See more options in + /// [See Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.fill_between.html) pub fn set_extra(&mut self, extra: &str) -> &mut Self { self.extra = extra.to_string(); self From 8b2763685fcf234648748a35e44b737141e2d1cb Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 11:15:29 +1000 Subject: [PATCH 09/20] Improve histogram doc --- src/histogram.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/histogram.rs b/src/histogram.rs index 9c4278e..a25fad3 100644 --- a/src/histogram.rs +++ b/src/histogram.rs @@ -2,7 +2,11 @@ use super::{generate_list_quoted, generate_nested_list, GraphMaker}; use num_traits::Num; use std::fmt::Write; -/// Generates a Histogram plot +/// Generates a Histogram plot with support for multiple overlaid or stacked series +/// +/// Each inner `Vec` in `values` represents a data series. The `labels` slice +/// provides legend labels, one per series. Colors, style (bar, step, stacked), +/// and bin count are configurable. /// /// [See Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.hist.html) /// @@ -75,12 +79,12 @@ impl Histogram { } } - /// Draws histogram + /// Draws histogram with one or more data series /// /// # Input /// - /// * `values` -- holds the values - /// * `labels` -- holds the labels + /// * `values` -- a nested list where each inner `Vec` is a data series + /// * `labels` -- legend labels, one per series (must match `values.len()`) pub fn draw(&mut self, values: &Vec>, labels: &[U]) where T: std::fmt::Display + Num, @@ -95,13 +99,13 @@ impl Histogram { write!(&mut self.buffer, "plt.hist(values,label=labels{})\n", &opt).unwrap(); } - /// Sets the colors for each bar + /// Sets the color for each data series (one color per series) pub fn set_colors(&mut self, colors: &[&str]) -> &mut Self { self.colors = colors.iter().map(|color| color.to_string()).collect(); self } - /// Sets the width of the lines + /// Sets the width of bar edge lines pub fn set_line_width(&mut self, width: f64) -> &mut Self { self.line_width = width; self @@ -133,7 +137,7 @@ impl Histogram { self } - /// Sets the number of bins + /// Sets number of bins (set to `0` for automatic bin calculation) pub fn set_number_bins(&mut self, bins: usize) -> &mut Self { self.number_bins = bins; self From 639ed07791e9ee627054feacb03c7c4efc4e1499 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 11:30:36 +1000 Subject: [PATCH 10/20] Improve image doc --- src/image.rs | 56 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/src/image.rs b/src/image.rs index 903dfd2..fdbaf93 100644 --- a/src/image.rs +++ b/src/image.rs @@ -4,7 +4,11 @@ use std::fmt::Write; /// Generates an image plot (imshow) /// -/// [See Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.imshow.html) +/// Uses Matplotlib's [imshow](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.imshow.html)) +/// +/// Displays a 2D matrix as a heatmap with configurable colormap. +/// Supports both scalar data (via [`draw`](Self::draw)) and RGB/RGBA channel data +/// (via [`draw_rgb_or_rgba`](Self::draw_rgb_or_rgba)). /// /// # Examples /// @@ -54,13 +58,13 @@ impl Image { } } - /// (imshow) Displays data as an image + /// (imshow) Displays scalar data as a colored image / heatmap /// - /// # Arguments + /// # Input /// - /// * `data` - 2D matrix-like data structure + /// * `data` -- 2D matrix of scalar values (rows × columns) /// - /// See + /// See also: [`draw_rgb_or_rgba`](Self::draw_rgb_or_rgba) for RGB/RGBA images pub fn draw<'a, T, U>(&mut self, data: &'a T) where T: AsMatrix<'a, U>, @@ -88,37 +92,47 @@ impl Image { write!(&mut self.buffer, "plt.imshow(data{})\n", &opt).unwrap(); } - /// Sets the colormap index + /// Sets a colormap by index from a built-in list + /// + /// Indices wrap around: /// - /// Options: + /// | Index | Colormap | + /// |-------|----------| + /// | 0 | bwr | + /// | 1 | RdBu | + /// | 2 | hsv | + /// | 3 | jet | + /// | 4 | terrain | + /// | 5 | pink | + /// | 6 | Greys | /// - /// * 0 -- bwr - /// * 1 -- RdBu - /// * 2 -- hsv - /// * 3 -- jet - /// * 4 -- terrain - /// * 5 -- pink - /// * 6 -- Greys - /// * `>`6 -- starts over from 0 + /// See also: [`set_colormap_name`](Self::set_colormap_name) 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 } - /// Sets the colormap name + /// Sets the colormap by name (e.g., `"terrain"`, `"jet"`, `"viridis"`) /// - /// Colormap names: + /// See the [Matplotlib colormap reference](https://matplotlib.org/stable/tutorials/colors/colormaps.html) + /// for all available options. /// - /// * see - /// - /// Will use `colormap_index` instead if `colormap_name` is empty. + /// See also: [`set_colormap_index`](Self::set_colormap_index) pub fn set_colormap_name(&mut self, name: &str) -> &mut Self { self.colormap_name = String::from(name); self } - // Sets extra python/matplotlib commands (comma separated) + /// Sets extra matplotlib commands (comma separated) + /// + /// **Important:** The extra commands must be comma separated. For example: + /// + /// ```text + /// param1=123,param2='hello' + /// ``` + /// + /// [See Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.imshow.html) pub fn set_extra(&mut self, extra: &str) -> &mut Self { self.extra = extra.to_string(); self From c2bb610326648ef84911a3b3966db35e4c7c9d32 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 11:31:42 +1000 Subject: [PATCH 11/20] Improve inset_axes doc --- src/inset_axes.rs | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/inset_axes.rs b/src/inset_axes.rs index 0af7fe8..5adb80e 100644 --- a/src/inset_axes.rs +++ b/src/inset_axes.rs @@ -1,7 +1,11 @@ use super::GraphMaker; use std::fmt::Write; -/// Implements the capability to add inset Axes to existing Axes. +/// Implements inset Axes — a magnified sub-view of a region within the main plot +/// +/// An inset is a smaller axes placed inside the main axes, connected by indicator +/// lines to show which region is being magnified. Use [`add`](Self::add) to place +/// graph entities (curves, contours, etc.) inside the inset, just like with [`Plot`](crate::Plot). /// /// # Examples /// @@ -235,27 +239,38 @@ impl InsetAxes { self } - /// Sets extra Matplotlib commands for the inset Axes (comma separated). + /// Sets extra matplotlib commands (comma separated) for the inset Axes + /// + /// **Important:** The extra commands must be comma separated. For example: /// - /// [See Matplotlib's documentation for extra parameters]() + /// ```text + /// param1=123,param2='hello' + /// ``` + /// + /// [See Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.inset_axes.html) pub fn set_extra_for_axes(&mut self, extra: &str) -> &mut Self { self.extra_for_axes = extra.to_string(); self } - /// Sets extra Matplotlib commands for the indicator (comma separated). + /// Sets extra matplotlib commands (comma separated) for the indicator/connector lines + /// + /// **Important:** The extra commands must be comma separated. For example: /// - /// [See Matplotlib's documentation for extra parameters](https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.indicate_inset.html#matplotlib.axes.Axes.indicate_inset) + /// ```text + /// param1=123,param2='hello' + /// ``` + /// + /// [See Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.indicate_inset.html#matplotlib.axes.Axes.indicate_inset) pub fn set_extra_for_indicator(&mut self, extra: &str) -> &mut Self { self.extra_for_indicator = extra.to_string(); self } - /// Sets the visibility of the axes ticks - /// - /// # Arguments + /// Controls whether axes ticks are shown inside the inset /// - /// * `visible` - If true, shows the axes ticks. If false, hides them. + /// When `false` (default), both x and y ticks are hidden for a cleaner look. + /// Set to `true` to show the tick marks and labels. pub fn set_visibility(&mut self, visible: bool) -> &mut Self { self.axes_visible = visible; self From 032bb8459fa3e0d4d0a46f87d26cd8be47373895 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 11:33:28 +1000 Subject: [PATCH 12/20] Improve legend doc --- src/legend.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/legend.rs b/src/legend.rs index f2c8c7c..6c1f7d5 100644 --- a/src/legend.rs +++ b/src/legend.rs @@ -1,7 +1,11 @@ use super::{generate_list, GraphMaker}; use std::fmt::Write; -/// Generates a Legend +/// Generates a Legend from all labelled graph entities in the current axes +/// +/// The legend automatically collects labels from preceding entities (e.g., curves with +/// [`set_label`](crate::Curve::set_label)). It can be placed inside or outside +/// the plot area and supports multi-column layout. /// /// [See Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.legend.html) /// @@ -95,7 +99,10 @@ impl Legend { } } - /// Draws legend + /// Draws the legend, collecting labels from all preceding graph entities + /// + /// Call this after adding all labelled curves/surfaces/etc. to the plot buffer. + /// The legend only appears if at least one entity has a label set. pub fn draw(&mut self) { let opt = self.options(); if self.outside { @@ -110,19 +117,19 @@ impl Legend { } } - /// Sets the fontsize + /// Sets the font size for legend text pub fn set_fontsize(&mut self, fontsize: f64) -> &mut Self { self.fontsize = fontsize; self } - /// Sets the length of legend's indicator line + /// Sets the length of the indicator line next to each legend entry pub fn set_handle_len(&mut self, length: f64) -> &mut Self { self.handle_len = length; self } - /// Sets the number of columns + /// Arranges legend entries in multiple columns instead of a single column pub fn set_num_col(&mut self, num_columns: usize) -> &mut Self { self.num_col = num_columns; self @@ -139,7 +146,10 @@ impl Legend { self } - /// Sets option to put legend outside of plot area + /// Places the legend outside the plot area (to the right) + /// + /// The positioning is controlled by [`set_x_coords`](Self::set_x_coords). + /// Default coordinates place it just outside the right edge. pub fn set_outside(&mut self, flag: bool) -> &mut Self { self.outside = flag; self @@ -151,9 +161,11 @@ impl Legend { self } - /// Sets the normalized coordinates when drawing an outside legend + /// Sets the anchor coordinates for an outside legend + /// + /// Uses `[anchor_x0, anchor_y0, size_x, size_y]` in normalized figure units. /// - /// Example: `[0.0, 1.02, 1.0, 0.102]` + /// Default: `[0.0, 1.02, 1.0, 0.102]` pub fn set_x_coords(&mut self, coords: &[f64]) -> &mut Self { self.x_coords = coords.to_vec(); self From 068289876b0a79c65f295c86edf1c571c3181380 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 11:35:57 +1000 Subject: [PATCH 13/20] Improve plot doc --- src/plot.rs | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/plot.rs b/src/plot.rs index a5a6111..5b1809e 100644 --- a/src/plot.rs +++ b/src/plot.rs @@ -18,7 +18,17 @@ pub trait GraphMaker { fn clear_buffer(&mut self); } -/// Driver structure that calls Python +/// Central plot driver — collects graph entities, generates a Python script, and executes it +/// +/// The `Plot` struct is the main entry point. The workflow is: +/// +/// 1. Create and configure graph entities ([`Curve`](crate::Curve), [`Surface`](crate::Surface), etc.) +/// 2. Call their `draw()` methods (builds Python code into their buffer) +/// 3. `plot.add(&entity)` to concatenate entity buffers +/// 4. Configure axes, ticks, labels, subplots via `set_*` methods +/// 5. `plot.save(path)` or `plot.show(path)` to write the `.py` file and run Python +/// +/// All configuration methods return `&mut Self` for method chaining. /// /// # Examples /// @@ -184,16 +194,18 @@ impl Plot { self } - /// Tells matplotlib to try to figure out the tight bounding box of the figure (default = true) + /// Enables tight bounding box calculation when saving (default: `true`) + /// + /// When `true`, Matplotlib automatically crops whitespace around the figure. + /// Disable if cropping cuts off labels or other elements. pub fn set_save_tight(&mut self, tight: bool) -> &mut Self { self.save_tight = tight; self } - /// Sets the padding around the figure when the 'tight' layout is enabled during saving + /// Sets extra padding (in inches) around the figure when tight layout is enabled /// - /// This option may circumvent *rare* problems when matplotlib fails to compute the best bounding box - /// (e.g., when the labels of 3D plots are ignored) + /// Useful when the automatic tight bounding box clips 3D plot labels or other edge elements. pub fn set_save_pad_inches(&mut self, pad_inches: f64) -> &mut Self { self.save_pad_inches = Some(pad_inches); self @@ -207,6 +219,8 @@ impl Plot { /// Calls Python and saves the python script and figure /// + /// The figure format is determined by the file extension (`.svg`, `.png`, `.pdf`, etc.). + /// /// # Input /// /// * `figure_path` -- may be a String, &str, or Path @@ -215,6 +229,7 @@ impl Plot { /// /// 1. You may want to call [Plot::set_show_errors()] to enable the /// display of Python errors (if any) + /// 2. The intermediate Python script is saved alongside the figure with a `.py` extension pub fn save(&self, figure_path: &S) -> Result<(), StrError> where S: AsRef + ?Sized, @@ -279,7 +294,10 @@ impl Plot { self } - /// Adds legend to plot (see Legend for further options) + /// Adds legend to the current axes using default Legend settings + /// + /// For more control (fontsize, columns, outside placement), create a [`Legend`] yourself + /// and add it via [`add`](Self::add). pub fn legend(&mut self) -> &mut Self { let mut legend = Legend::new(); legend.draw(); @@ -616,7 +634,7 @@ impl Plot { self } - /// Sets number of ticks along x + /// Sets number of ticks along x (set to `0` to remove all ticks) pub fn set_num_ticks_x(&mut self, num: usize) -> &mut Self { if num == 0 { self.buffer.push_str("plt.gca().get_xaxis().set_ticks([])\n"); @@ -631,7 +649,7 @@ impl Plot { self } - /// Sets number of ticks along y + /// Sets number of ticks along y (set to `0` to remove all ticks) pub fn set_num_ticks_y(&mut self, num: usize) -> &mut Self { if num == 0 { self.buffer.push_str("plt.gca().get_yaxis().set_ticks([])\n"); @@ -646,7 +664,7 @@ impl Plot { self } - /// Sets number of ticks along z + /// Sets number of ticks along z (set to `0` to remove all ticks) pub fn set_num_ticks_z(&mut self, num: usize) -> &mut Self { if num == 0 { self.buffer.push_str("plt.gca().get_zaxis().set_ticks([])\n"); @@ -1136,7 +1154,10 @@ impl Plot { self } - /// Writes extra python commands + /// Appends arbitrary Python commands to the script + /// + /// Use this to inject custom Matplotlib commands not yet covered by the library. + /// Unlike `set_extra` methods on graph entities, this inserts raw Python. pub fn extra(&mut self, commands: &str) -> &mut Self { self.buffer.write_str(commands).unwrap(); self From 5e5d02a31913a36ac0e951a034f3822fd1cd96db Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 11:37:19 +1000 Subject: [PATCH 14/20] Improve slope_icon doc --- src/slope_icon.rs | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/slope_icon.rs b/src/slope_icon.rs index fb5aa5c..89c1213 100644 --- a/src/slope_icon.rs +++ b/src/slope_icon.rs @@ -1,7 +1,11 @@ use super::GraphMaker; use std::fmt::Write; -/// Creates an icon to indicate the slope of lines +/// Creates an icon indicating the slope of a line at a given point +/// +/// Draws a small triangle with annotated horizontal (run = 1) and vertical +/// (rise = slope) labels. Works correctly with both linear and log-scale axes. +/// Handles positive and negative slopes, and can be flipped above or below the line. /// /// # Notes /// @@ -116,7 +120,13 @@ impl SlopeIcon { } } - /// Draws an icon of line slope + /// Draws the slope indicator icon at a given position + /// + /// # Input + /// + /// * `slope` -- the line slope (rise / run) + /// * `x_center` -- x-coordinate of the icon center (data coordinates) + /// * `y_center` -- y-coordinate of the icon center (data coordinates) pub fn draw(&mut self, slope: f64, x_center: f64, y_center: f64) { // set flip flag let flip = if slope < 0.0 { !self.above } else { self.above }; @@ -241,7 +251,7 @@ impl SlopeIcon { } } - /// Sets option to draw icon above line + /// Flips the icon to appear above the line instead of below pub fn set_above(&mut self, flag: bool) -> &mut Self { self.above = flag; self @@ -300,19 +310,24 @@ impl SlopeIcon { self } - /// Sets the the precision of slope number in label + /// Sets the number of decimal places for the automatic slope label + /// + /// Set to `0` (default) for full precision display. pub fn set_precision(&mut self, value: usize) -> &mut Self { self.precision = value; self } - /// Sets text of horizontal value (== 1) + /// Overrides the horizontal label text (default: `"1"`, representing "run = 1") pub fn set_text_h(&mut self, one: &str) -> &mut Self { self.text_h = String::from(one); self } - /// Sets text of vertical value (slope) + /// Overrides the vertical label text (default: auto-calculated from slope) + /// + /// Set this to display a custom label instead of the numerical slope value + /// (e.g., `"λ"`, `"m"`, or a fixed number). pub fn set_text_v(&mut self, slope: &str) -> &mut Self { self.text_v = String::from(slope); self From dc455d749e65f0a9f9b380c97325603502cac838 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 11:38:29 +1000 Subject: [PATCH 15/20] Improve stream doc --- src/stream.rs | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/stream.rs b/src/stream.rs index f42c1d8..5fb833b 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -4,7 +4,12 @@ use crate::{AsMatrix, AsVector}; use num_traits::Num; use std::fmt::Write; -/// Implements functions to illustrate vector fields using streamlines and quiver plots +/// Visualizes vector fields using [streamlines](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.streamplot.html) +/// and [quiver arrow](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.quiver.html) plots +/// +/// Supports two input formats for each plot type: +/// - **Matrices** (`draw` / `draw_arrows`) — 2D arrays for coordinates and components +/// - **Vectors** (`draw_alt` / `draw_arrows_alt`) — 1D coordinate arrays + 2D component matrices pub struct Stream { // common options color: String, @@ -166,7 +171,7 @@ impl Stream { self } - /// Sets the density of streamlines + /// Sets the density of streamlines (higher = more lines) pub fn set_streamplot_density(&mut self, density: f64) -> &mut Self { self.streamplot_density = density; self @@ -178,15 +183,24 @@ impl Stream { self } - /// Sets extra options for streamlines + /// Sets extra matplotlib commands (comma separated) for the streamplot /// - /// See + /// **Important:** The extra commands must be comma separated. For example: + /// + /// ```text + /// param1=123,param2='hello' + /// ``` + /// + /// [See Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.streamplot.html) pub fn set_streamplot_extra(&mut self, extra: &str) -> &mut Self { self.streamplot_extra = extra.to_string(); self } - /// Sets the quiver inverse scale + /// Sets the quiver scale factor (larger values produce smaller arrows) + /// + /// This is the inverse of the arrow scaling. Increase to shrink arrows, + /// decrease to enlarge them. pub fn set_quiver_inv_scale(&mut self, scale: f64) -> &mut Self { self.quiver_scale = scale; self @@ -208,9 +222,15 @@ impl Stream { self } - /// Sets extra options for quiver + /// Sets extra matplotlib commands (comma separated) for the quiver plot + /// + /// **Important:** The extra commands must be comma separated. For example: + /// + /// ```text + /// param1=123,param2='hello' + /// ``` /// - /// See + /// [See Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.quiver.html) pub fn set_quiver_extra(&mut self, extra: &str) -> &mut Self { self.quiver_extra = extra.to_string(); self From fbb522ced553a31448ee09ae1df72cdd4eb06700 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 11:41:41 +1000 Subject: [PATCH 16/20] Improve the doc for dark_mode, super title, surface, and text --- src/dark_mode.rs | 17 +++++++++++++---- src/super_title_params.rs | 7 +++++-- src/surface.rs | 7 ++++++- src/text.rs | 25 ++++++++++++++++++++++--- 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/dark_mode.rs b/src/dark_mode.rs index 9c5f0c7..781a5ee 100644 --- a/src/dark_mode.rs +++ b/src/dark_mode.rs @@ -2,15 +2,24 @@ use super::GraphMaker; /// Implements a dark mode enabler for plots /// -/// **Warning;** This instance must be the **first** to be added to the `Plot` object, +/// **Warning:** This instance must be the **first** added to the `Plot` object +/// (before any curves, surfaces, etc.) so that the style is applied before any +/// drawing commands. +/// +/// Available themes: +/// - `set_dark_background()` — Matplotlib's built-in dark theme (default) +/// - `set_mathematica()` — Mathematica-like color scheme +/// - `set_mocha()` — Catppuccin Mocha color scheme +/// - `set_nordic()` — Nordic Night color scheme +/// +/// **Note:** `set_mathematica`, `set_mocha`, and `set_nordic` require the `cycler` +/// package in your Python environment. pub struct DarkMode { buffer: String, } impl DarkMode { - /// Allocates a new instance - /// - /// **Warning;** This instance must be the **first** to be added to the `Plot` object, + /// Allocates a new instance with the default `dark_background` theme pub fn new() -> Self { let mut dm = DarkMode { buffer: String::new() }; dm.set_dark_background(); diff --git a/src/super_title_params.rs b/src/super_title_params.rs index acd6b73..8c912b4 100644 --- a/src/super_title_params.rs +++ b/src/super_title_params.rs @@ -1,6 +1,9 @@ use std::fmt::Write; -/// Holds parameters for the SuperTitle +/// Holds formatting parameters for the figure-level supertitle +/// +/// Created independently and passed to [`Plot::set_super_title`](crate::Plot::set_super_title). +/// All fields are optional — only set values are emitted to the Python script. #[derive(Clone)] pub struct SuperTitleParams { /// The x location of the text in figure coordinates (default = 0.5) @@ -73,7 +76,7 @@ impl SuperTitleParams { self } - /// Sets the font weight of the text + /// Sets the font weight (numeric: 400 = normal, 700 = bold) pub fn set_fontweight(&mut self, value: f64) -> &mut Self { self.fontweight = value; self diff --git a/src/surface.rs b/src/surface.rs index 252d5f6..24771e4 100644 --- a/src/surface.rs +++ b/src/surface.rs @@ -3,7 +3,12 @@ use crate::quote_marker; use num_traits::Num; use std::fmt::Write; -/// Generates a 3D a surface (or wireframe, or both) +/// Generates a 3D surface (or wireframe, or both) +/// +/// Can render a surface, a wireframe overlay, scatter points, and a colorbar +/// from the same (x, y, z) matrix data. Higher-level geometry primitives +/// (cylinder, sphere, plane, etc.) are available via [`Surface::draw_cylinder`], +/// [`Surface::draw_sphere`], and related methods. /// /// [See Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/mpl_toolkits.mplot3d.axes3d.Axes3D.plot_surface.html) /// diff --git a/src/text.rs b/src/text.rs index e92e138..d5c50f6 100644 --- a/src/text.rs +++ b/src/text.rs @@ -2,7 +2,10 @@ use super::GraphMaker; use num_traits::Num; use std::fmt::Write; -/// Creates text to be added to a plot +/// Creates text annotations for 2D or 3D plots with optional bounding boxes +/// +/// Supports [`draw`](Self::draw) for 2D and [`draw_3d`](Self::draw_3d) for 3D placement. +/// Optional bounding boxes can be styled via the `set_bbox_*` methods. /// /// [See Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.text.html) /// @@ -79,7 +82,14 @@ impl Text { } } - /// Draws text + /// Draws text at (x, y) in a 2D plot + /// + /// # Input + /// + /// * `x`, `y` -- coordinates (data coordinates) + /// * `message` -- text string to display + /// + /// See also: [`draw_3d`](Self::draw_3d) for 3D text pub fn draw(&mut self, x: T, y: T, message: &str) where T: std::fmt::Display + Num, @@ -92,7 +102,16 @@ impl Text { } } - /// Draws text in 3D plot + /// Draws text at (x, y, z) in a 3D plot + /// + /// Requires [`Plot::set_subplot_3d`](crate::Plot::set_subplot_3d) to be called first. + /// + /// # Input + /// + /// * `x`, `y`, `z` -- coordinates (data coordinates) + /// * `message` -- text string to display + /// + /// See also: [`draw`](Self::draw) for 2D text pub fn draw_3d(&mut self, x: T, y: T, z: T, message: &str) where T: std::fmt::Display + Num, From 5b7a093550625c910b943d923739ee7edc0323de Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 14:15:20 +1000 Subject: [PATCH 17/20] Make the documentation consistent across all files --- src/as_vector.rs | 2 +- src/canvas.rs | 35 ++++++++++++++++++++++++++++------- src/contour.rs | 12 +++++++++--- src/curve.rs | 4 +++- src/dark_mode.rs | 4 +++- src/image.rs | 4 ++-- src/inset_axes.rs | 6 +++--- src/plot.rs | 8 ++++---- src/surface.rs | 2 +- src/surface_geometry.rs | 2 +- 10 files changed, 55 insertions(+), 24 deletions(-) diff --git a/src/as_vector.rs b/src/as_vector.rs index 0f32765..01b5ea5 100644 --- a/src/as_vector.rs +++ b/src/as_vector.rs @@ -108,7 +108,7 @@ mod tests { let y: &[f64] = &[10.0, 20.0, 30.0]; assert_eq!(vector_str(&y), "10,20,30,\n"); - // stack-allocated (fixed-size) 2D array + // stack-allocated (fixed-size) 1D array let z = [100.0, 200.0, 300.0]; assert_eq!(vector_str(&z), "100,200,300,\n"); } diff --git a/src/canvas.rs b/src/canvas.rs index d0c1d92..68b9f58 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -156,11 +156,11 @@ pub struct Canvas { glyph_line_width: f64, // Line width for 3D glyphs glyph_size: f64, // Size for 3D glyphs glyph_color_x: String, // Color for X axis of 3D glyphs - glyph_color_y: String, // Color for X axis of 3D glyphs - glyph_color_z: String, // Color for X axis of 3D glyphs + glyph_color_y: String, // Color for Y axis of 3D glyphs + glyph_color_z: String, // Color for Z axis of 3D glyphs glyph_label_x: String, // Label for X axis of 3D glyphs - glyph_label_y: String, // Label for X axis of 3D glyphs - glyph_label_z: String, // Label for X axis of 3D glyphs + glyph_label_y: String, // Label for Y axis of 3D glyphs + glyph_label_z: String, // Label for Z axis of 3D glyphs glyph_label_color: String, // Color for labels of 3D glyphs (overrides individual axis colors) glyph_bbox_opt: String, // Python options for the dictionary setting the bounding box of 3D glyphs' text @@ -279,6 +279,11 @@ impl Canvas { /// Draws triangles (2D only) /// + /// # Input + /// + /// * `xx`, `yy` -- point coordinates (1D arrays) + /// * `connectivity` -- triangulation connectivity matrix (n_triangles × 3) + /// /// Using pub fn draw_triangles<'a, T, U, C>(&mut self, xx: &'a T, yy: &'a T, connectivity: &'a C) -> &mut Self where @@ -296,9 +301,14 @@ impl Canvas { /// Draws triangles (3D only) /// - /// Using + /// # Input + /// + /// * `xx`, `yy`, `zz` -- point coordinates (1D arrays) + /// * `connectivity` -- triangulation connectivity matrix (n_triangles × 3) /// /// Note: There is no way to set shading and facecolor at the same time. + /// + /// Using pub fn draw_triangles_3d<'a, T, U, C>(&mut self, xx: &'a T, yy: &'a T, zz: &'a T, connectivity: &'a C) -> &mut Self where T: AsVector<'a, U>, @@ -404,6 +414,12 @@ impl Canvas { /// Draws polyline with straight segments, quadratic Bezier, or cubic Bezier (2D only) /// + /// # Input + /// + /// * `points` -- (n × 2) matrix of `(x, y)` coordinates + /// * `codes` -- slice of [`PolyCode`] values, one per point + /// * `closed` -- if `true`, closes the path back to the first point + /// /// **Note:** The first and last commands are ignored. pub fn draw_polycurve<'a, T, U>(&mut self, points: &'a T, codes: &[PolyCode], closed: bool) -> Result<(), StrError> where @@ -504,6 +520,11 @@ impl Canvas { /// Draws polyline (2D or 3D, auto-detected from point dimensions) /// + /// # Input + /// + /// * `points` -- (n × 2) or (n × 3) matrix of coordinates + /// * `closed` -- if `true`, connects the last point back to the first + /// /// If points are `2D` (`ndim == 2`), uses 2D path patches (supporting /// `face_color` and `edge_color` fills). If `3D` (`ndim == 3`), draws as /// a 3D line plot. @@ -831,7 +852,7 @@ impl Canvas { /// /// Options: /// - /// * "`-`", `:`", "`--`", "`-.`", or "`None`" + /// * "`-`", "`:`", "`--`", "`-.`", or "`None`" /// * As defined in pub fn set_line_style(&mut self, style: &str) -> &mut Self { self.line_style = String::from(style); @@ -1040,7 +1061,7 @@ impl Canvas { opt } - /// Returns shared options + /// Returns options for triangles (3D only) fn options_triangles_3d(&self) -> String { let mut opt = String::new(); if self.edge_color != "" { diff --git a/src/contour.rs b/src/contour.rs index 44d097e..b3eb29b 100644 --- a/src/contour.rs +++ b/src/contour.rs @@ -323,7 +323,7 @@ impl Contour { /// Configure the axes into which the colorbar will be drawn /// - /// # Arguments + /// # Input /// /// * `location` -- location of the colorbar axes (e.g., 'right', 'top', 'left', 'bottom') /// * `width_pct` -- width percentage of the colorbar axes (e.g., 5.0, 10.0, 15.0) @@ -349,9 +349,15 @@ impl Contour { self } - /// Sets extra options for the colorbar + /// Sets extra matplotlib commands (comma separated) for the colorbar + /// + /// **Important:** The extra commands must be comma separated. For example: + /// + /// ```text + /// fraction=0.046,pad=0.04 + /// ``` /// - /// Example `extra = "fraction=0.046, pad=0.04"` + /// [See Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.colorbar.html) pub fn set_colorbar_extra(&mut self, extra: &str) -> &mut Self { self.colorbar_extra = String::from(extra); self diff --git a/src/curve.rs b/src/curve.rs index 6429ecf..ccbd428 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -300,6 +300,8 @@ impl Curve { /// Configure the twin-x label and color via [`Plot::set_label_y_twinx`](crate::Plot::set_label_y_twinx) /// and [`Plot::set_label_y_twinx_color`](crate::Plot::set_label_y_twinx_color). /// + /// # Input + /// /// * `y` -- ordinate values for the right-hand side axis pub fn draw_with_twin_x<'a, T, U>(&mut self, y: &'a T) where @@ -394,7 +396,7 @@ impl Curve { /// /// Options: /// - /// * "`-`", `:`", "`--`", "`-.`", or "`None`" + /// * "`-`", "`:`", "`--`", "`-.`", or "`None`" /// * As defined in pub fn set_line_style(&mut self, style: &str) -> &mut Self { self.line_style = String::from(style); diff --git a/src/dark_mode.rs b/src/dark_mode.rs index 781a5ee..45f36cc 100644 --- a/src/dark_mode.rs +++ b/src/dark_mode.rs @@ -26,7 +26,7 @@ impl DarkMode { dm } - /// Sets the Matplotlib native dark mode (dark_background) + /// Applies Matplotlib's built-in `dark_background` style pub fn set_dark_background(&mut self) { self.buffer.clear(); self.buffer.push_str("plt.style.use('dark_background')\n"); @@ -81,6 +81,8 @@ plt.rcParams.update({ ); } + /// Sets the Catppuccin Mocha dark color scheme + /// /// **Important:** This mode requires `cycler` package in Python environment. pub fn set_mocha(&mut self) { self.buffer.clear(); diff --git a/src/image.rs b/src/image.rs index fdbaf93..be04c10 100644 --- a/src/image.rs +++ b/src/image.rs @@ -77,7 +77,7 @@ impl Image { /// (imshow) Displays data as an image with RGB or RGB(A) values /// - /// # Arguments + /// # Input /// /// * `data` - 3D vector with shape (height, width, 3) for RGB or (height, width, 4) for RGBA /// The inner-most vector contains the color channels. @@ -138,7 +138,7 @@ impl Image { self } - /// Returns options for barplot + /// Returns options for image fn options(&self) -> String { let mut opt = String::new(); if self.colormap_name != "" { diff --git a/src/inset_axes.rs b/src/inset_axes.rs index 5adb80e..4a26f99 100644 --- a/src/inset_axes.rs +++ b/src/inset_axes.rs @@ -133,7 +133,7 @@ impl InsetAxes { /// * "." - dot hatching /// * "*" - star hatching /// - /// [See options in ](https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.indicate_inset.html#matplotlib.axes.Axes.indicate_inset) + /// [See Matplotlib's documentation](https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.indicate_inset.html#matplotlib.axes.Axes.indicate_inset) /// /// [See Matplotlib's documentation for more hatch patterns](https://matplotlib.org/stable/gallery/shapes_and_collections/hatch_demo.html) pub fn set_indicator_hatch(&mut self, hatch: &str) -> &mut Self { @@ -190,7 +190,7 @@ impl InsetAxes { /// /// Example of normalized coordinates: `(0.5, 0.5, 0.4, 0.3)`. /// - /// # Arguments + /// # Input /// /// * `u0` -- The normalized (0 to 1) horizontal figure coordinate of the lower-left corner of the inset Axes. /// * `v0` -- The normalized (0 to 1) vertical figure coordinate of the lower-left corner of the inset Axes. @@ -284,7 +284,7 @@ impl InsetAxes { /// Sets whether the indicator lines are disabled /// - /// # Arguments + /// # Input /// /// * `disabled` - If true, hides the indicator lines. If false, shows them. pub fn set_indicator_disabled(&mut self, disabled: bool) -> &mut Self { diff --git a/src/plot.rs b/src/plot.rs index 5b1809e..fee9093 100644 --- a/src/plot.rs +++ b/src/plot.rs @@ -684,7 +684,7 @@ impl Plot { /// # Input /// /// * `major_every` -- step for major ticks (ignored if ≤ 0.0) - /// * `minor_every` -- step for major ticks (ignored if ≤ 0.0) + /// * `minor_every` -- step for minor ticks (ignored if ≤ 0.0) /// * `major_number_format` -- C-style number format for major ticks; e.g. "%.2f" (ignored if empty "") /// See [matplotlib FormatStrFormatter](https://matplotlib.org/stable/api/ticker_api.html#matplotlib.ticker.FormatStrFormatter) #[rustfmt::skip] @@ -713,7 +713,7 @@ impl Plot { /// # Input /// /// * `major_every` -- step for major ticks (ignored if ≤ 0.0) - /// * `minor_every` -- step for major ticks (ignored if ≤ 0.0) + /// * `minor_every` -- step for minor ticks (ignored if ≤ 0.0) /// * `major_number_format` -- C-style number format for major ticks; e.g. "%.2f" (ignored if empty "") /// See [matplotlib FormatStrFormatter](https://matplotlib.org/stable/api/ticker_api.html#matplotlib.ticker.FormatStrFormatter) #[rustfmt::skip] @@ -833,7 +833,7 @@ impl Plot { /// /// # Input /// - /// * `minor_every` -- step for major ticks (ignored if ≤ 0.0). Example `PI / 12.0` + /// * `minor_every` -- step for minor ticks (ignored if ≤ 0.0). Example `PI / 12.0` /// /// **Note:** This function sets the major ticks as `PI / 2.0`. #[rustfmt::skip] @@ -858,7 +858,7 @@ impl Plot { /// /// # Input /// - /// * `minor_every` -- step for major ticks (ignored if ≤ 0.0). Example `PI / 12.0` + /// * `minor_every` -- step for minor ticks (ignored if ≤ 0.0). Example `PI / 12.0` /// /// **Note:** This function sets the major ticks as `PI / 2.0`. #[rustfmt::skip] diff --git a/src/surface.rs b/src/surface.rs index 24771e4..76dc723 100644 --- a/src/surface.rs +++ b/src/surface.rs @@ -211,7 +211,7 @@ impl Surface { /// * `terrain` /// * `pink` /// * `Greys` - /// * see more here + /// * See the [Matplotlib colormap reference](https://matplotlib.org/stable/tutorials/colors/colormaps.html) for all available options pub fn set_colormap_name(&mut self, name: &str) -> &mut Self { self.colormap_name = String::from(name); self diff --git a/src/surface_geometry.rs b/src/surface_geometry.rs index d8d49d0..301944d 100644 --- a/src/surface_geometry.rs +++ b/src/surface_geometry.rs @@ -358,7 +358,7 @@ impl Surface { /// * `n_alpha` -- number of divisions along α (must be ≥ 2) /// * `n_theta` -- number of divisions along θ (must be ≥ 2) /// - /// # Output: + /// # Output /// /// * `x`, `y`, `z` -- the coordinates of all points as in a meshgrid /// From 75956f2c76c3de81ef9199ce7ff97b7ff6970ff5 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 14:19:24 +1000 Subject: [PATCH 18/20] Rename GitHub action scripts --- .github/workflows/{test_on_arch_linux.yml => arch.yml} | 2 +- .github/workflows/{test_and_coverage.yml => ubuntu.yml} | 2 +- .vscode/settings.json | 4 ++++ README.md | 3 ++- 4 files changed, 8 insertions(+), 3 deletions(-) rename .github/workflows/{test_on_arch_linux.yml => arch.yml} (97%) rename .github/workflows/{test_and_coverage.yml => ubuntu.yml} (96%) diff --git a/.github/workflows/test_on_arch_linux.yml b/.github/workflows/arch.yml similarity index 97% rename from .github/workflows/test_on_arch_linux.yml rename to .github/workflows/arch.yml index 0e4b8d2..81fb8e5 100644 --- a/.github/workflows/test_on_arch_linux.yml +++ b/.github/workflows/arch.yml @@ -1,4 +1,4 @@ -name: Test +name: Arch on: [pull_request] jobs: test_on_arch_linux: diff --git a/.github/workflows/test_and_coverage.yml b/.github/workflows/ubuntu.yml similarity index 96% rename from .github/workflows/test_and_coverage.yml rename to .github/workflows/ubuntu.yml index 53c9539..d18e51e 100644 --- a/.github/workflows/test_and_coverage.yml +++ b/.github/workflows/ubuntu.yml @@ -1,4 +1,4 @@ -name: Test +name: Ubuntu on: [pull_request] jobs: test_and_coverage: diff --git a/.vscode/settings.json b/.vscode/settings.json index 2c0d869..a7bf9d7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,7 @@ }, "cSpell.words": [ "Adelie", + "archlinux", "arrowstyle", "axhline", "axisbelow", @@ -80,9 +81,12 @@ "meshgrid", "MOVETO", "mplot", + "nocapture", + "noconfirm", "nord", "numpoints", "numpy", + "pacman", "patheffects", "plotpy", "polyline", diff --git a/README.md b/README.md index a0e77c4..6baa729 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,8 @@ [![documentation](https://img.shields.io/badge/plotpy-documentation-blue)](https://docs.rs/plotpy) [![Track Awesome List](https://www.trackawesomelist.com/badge.svg)](https://www.trackawesomelist.com/rust-unofficial/awesome-rust/) -[![Test & Coverage](https://github.com/cpmech/plotpy/actions/workflows/test_and_coverage.yml/badge.svg)](https://github.com/cpmech/plotpy/actions/workflows/test_and_coverage.yml) +[![Arch](https://github.com/cpmech/plotpy/actions/workflows/arch.yml/badge.svg)](https://github.com/cpmech/plotpy/actions/workflows/arch.yml) +[![Ubuntu](https://github.com/cpmech/plotpy/actions/workflows/ubuntu.yml/badge.svg)](https://github.com/cpmech/plotpy/actions/workflows/ubuntu.yml) ## Contents From 09b264b705e858eba1ba07321eeaab0059c7b899 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 14:25:41 +1000 Subject: [PATCH 19/20] Improve README.md --- .vscode/settings.json | 1 + README.md | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index a7bf9d7..c876ed0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -89,6 +89,7 @@ "pacman", "patheffects", "plotpy", + "polycurve", "polyline", "pyplot", "rarrow", diff --git a/README.md b/README.md index 6baa729..5edf37c 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ On Debian/Ubuntu/Linux, run: sudo apt install python3-matplotlib ``` -**Important:** The Rust code will call `python3` via `std::process::Command`. However, there is an option to call a different python executable; for instance (the code below is no tested): +**Important:** The Rust code will call `python3` via `std::process::Command`. However, there is an option to call a different python executable; for instance (the code below is untested): ```text let mut plot = Plot::new(); @@ -81,7 +81,7 @@ plotpy = "*" Plotpy can be used with Jupyter via [evcxr](https://github.com/evcxr/evcxr). Thus, it can interactively display the plots in a Jupyter Notebook. This feature requires the installation of `evcxr`. See the [Jupyter/evcxr article](https://depth-first.com/articles/2020/09/21/interactive-rust-in-a-repl-and-jupyter-notebook-with-evcxr/). -The following code shows a minimal example (not tested) +The following code shows a minimal example (the code below is untested) ```text // set the python path @@ -102,6 +102,8 @@ plot.set_python_exe(python) ## Examples +Note, below `StrError` is defined as `pub type StrError = &'static str`; — a type alias for a static string slice. It's used throughout the library as the error type returned from functions. It's essentially a lightweight, allocation-free error type that avoids pulling in a full error-handling crate. + ### Barplot From ddd92b6a3955d3d6cbdb68ff3a9daa51c7e013c4 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 8 Jun 2026 14:30:44 +1000 Subject: [PATCH 20/20] Improve README.md --- README.md | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 5edf37c..83f5863 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,11 @@ - [Introduction](#introduction) - [Installation](#installation) -- [Setting Cargo.toml](#setting-cargotoml) -- [Use of Jupyter via evcxr](#use-of-jupyter-via-evcxr) + - [Arch Linux](#arch-linux) + - [Debian/Ubuntu Linux](#debianubuntu-linux) + - [Other systems](#other-systems) + - [Setting Cargo.toml](#setting-cargotoml) + - [Use of Jupyter via evcxr](#use-of-jupyter-via-evcxr) - [Examples](#examples) - [Barplot](#barplot) - [Boxplot](#boxplot) @@ -43,17 +46,29 @@ See also the [examples directory](https://github.com/cpmech/plotpy/tree/main/exa ## Installation -*This code is mainly tested on Debian/Ubuntu/Linux.* +*This code is mainly tested on Arch Linux and Debian/Ubuntu Linux.* -This crate needs Python3 and Matplotlib, of course. +This crate needs Python3 and Matplotlib. -On Debian/Ubuntu/Linux, run: +### Arch Linux + +Install the dependencies: + +```bash +pacman -Syu --noconfirm python-matplotlib +``` + +### Debian/Ubuntu Linux + +Install the dependencies: ```bash sudo apt install python3-matplotlib ``` -**Important:** The Rust code will call `python3` via `std::process::Command`. However, there is an option to call a different python executable; for instance (the code below is untested): +### Other systems + +It is possible to run `plotpy` in other systems where Python and Matplotlib are already installed. The Rust code calls `python3` via `std::process::Command`. However, there is an option to call a different python executable; for instance (the code below is untested): ```text let mut plot = Plot::new(); @@ -62,9 +77,7 @@ plot.set_python_exe("C:\Windows11\WhereIs\python.exe") .save(...)?; ``` - - -## Setting Cargo.toml +### Setting Cargo.toml [![Crates.io](https://img.shields.io/crates/v/plotpy.svg)](https://crates.io/crates/plotpy) @@ -75,9 +88,7 @@ plot.set_python_exe("C:\Windows11\WhereIs\python.exe") plotpy = "*" ``` - - -## Use of Jupyter via evcxr +### Use of Jupyter via evcxr Plotpy can be used with Jupyter via [evcxr](https://github.com/evcxr/evcxr). Thus, it can interactively display the plots in a Jupyter Notebook. This feature requires the installation of `evcxr`. See the [Jupyter/evcxr article](https://depth-first.com/articles/2020/09/21/interactive-rust-in-a-repl-and-jupyter-notebook-with-evcxr/).