From 0157566c53504c7a90c28ad9e26e9ca5e73f2d6e Mon Sep 17 00:00:00 2001 From: "Dorival Pedroso (aider)" Date: Mon, 10 Feb 2025 07:46:58 +1000 Subject: [PATCH 1/7] feat: add indicator_enabled toggle to InsetAxes --- src/inset_axes.rs | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/inset_axes.rs b/src/inset_axes.rs index 8c7a92b..ce3eed1 100644 --- a/src/inset_axes.rs +++ b/src/inset_axes.rs @@ -49,6 +49,7 @@ pub struct InsetAxes { indicator_hatch: String, indicator_alpha: Option, axes_visible: bool, + indicator_enabled: bool, title: String, buffer: String, } @@ -89,6 +90,7 @@ impl InsetAxes { indicator_hatch: String::new(), indicator_alpha: None, axes_visible: false, + indicator_enabled: true, title: String::new(), buffer: String::new(), } @@ -210,7 +212,9 @@ impl InsetAxes { if !self.title.is_empty() { write!(&mut self.buffer, "zoom.set_title(r'{}')\n", self.title).unwrap(); } - write!(&mut self.buffer, "plt.gca().indicate_inset_zoom(zoom{})\n", opt2,).unwrap(); + if self.indicator_enabled { + write!(&mut self.buffer, "plt.gca().indicate_inset_zoom(zoom{})\n", opt2,).unwrap(); + } } /// Sets the limits of axes in the inset. @@ -254,6 +258,16 @@ impl InsetAxes { self } + /// Sets whether the indicator lines are enabled + /// + /// # Arguments + /// + /// * `enabled` - If true, shows the indicator lines. If false, hides them. + pub fn set_indicator_enabled(&mut self, enabled: bool) -> &mut Self { + self.indicator_enabled = enabled; + self + } + /// Returns options for the inset Axes fn options_for_axes(&self) -> String { let mut opt = String::new(); @@ -374,6 +388,26 @@ mod tests { assert!(buffer.contains("plt.gca().indicate_inset_zoom(zoom")); } + #[test] + #[test] + fn test_indicator_enabled() { + let mut inset = InsetAxes::new(); + assert!(inset.indicator_enabled); + + inset.set_indicator_enabled(false); + assert!(!inset.indicator_enabled); + + inset.draw(0.5, 0.5, 0.4, 0.3); + let buffer = inset.get_buffer(); + assert!(!buffer.contains("indicate_inset_zoom")); + + inset.set_indicator_enabled(true); + inset.clear_buffer(); + inset.draw(0.5, 0.5, 0.4, 0.3); + let buffer = inset.get_buffer(); + assert!(buffer.contains("indicate_inset_zoom")); + } + #[test] fn test_clear_buffer() { let mut inset = InsetAxes::new(); From ba3705e4d15b946bad24538c1c5c1002df5d4b83 Mon Sep 17 00:00:00 2001 From: "Dorival Pedroso (aider)" Date: Mon, 10 Feb 2025 07:49:17 +1000 Subject: [PATCH 2/7] refactor: Rename `indicator_enabled` to `indicator_disabled` and flip logic --- src/inset_axes.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/inset_axes.rs b/src/inset_axes.rs index ce3eed1..e920f47 100644 --- a/src/inset_axes.rs +++ b/src/inset_axes.rs @@ -49,7 +49,7 @@ pub struct InsetAxes { indicator_hatch: String, indicator_alpha: Option, axes_visible: bool, - indicator_enabled: bool, + indicator_disabled: bool, title: String, buffer: String, } @@ -90,7 +90,7 @@ impl InsetAxes { indicator_hatch: String::new(), indicator_alpha: None, axes_visible: false, - indicator_enabled: true, + indicator_disabled: false, title: String::new(), buffer: String::new(), } @@ -212,7 +212,7 @@ impl InsetAxes { if !self.title.is_empty() { write!(&mut self.buffer, "zoom.set_title(r'{}')\n", self.title).unwrap(); } - if self.indicator_enabled { + if !self.indicator_disabled { write!(&mut self.buffer, "plt.gca().indicate_inset_zoom(zoom{})\n", opt2,).unwrap(); } } @@ -258,13 +258,13 @@ impl InsetAxes { self } - /// Sets whether the indicator lines are enabled + /// Sets whether the indicator lines are disabled /// /// # Arguments /// - /// * `enabled` - If true, shows the indicator lines. If false, hides them. - pub fn set_indicator_enabled(&mut self, enabled: bool) -> &mut Self { - self.indicator_enabled = enabled; + /// * `disabled` - If true, hides the indicator lines. If false, shows them. + pub fn set_indicator_disabled(&mut self, disabled: bool) -> &mut Self { + self.indicator_disabled = disabled; self } @@ -390,18 +390,18 @@ mod tests { #[test] #[test] - fn test_indicator_enabled() { + fn test_indicator_disabled() { let mut inset = InsetAxes::new(); - assert!(inset.indicator_enabled); + assert!(!inset.indicator_disabled); - inset.set_indicator_enabled(false); - assert!(!inset.indicator_enabled); + inset.set_indicator_disabled(true); + assert!(inset.indicator_disabled); inset.draw(0.5, 0.5, 0.4, 0.3); let buffer = inset.get_buffer(); assert!(!buffer.contains("indicate_inset_zoom")); - inset.set_indicator_enabled(true); + inset.set_indicator_disabled(false); inset.clear_buffer(); inset.draw(0.5, 0.5, 0.4, 0.3); let buffer = inset.get_buffer(); From 0539cc0e7161c8e15c55a256d7a7c4ca8673d8df Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 10 Feb 2025 07:52:03 +1000 Subject: [PATCH 3/7] Fix test --- src/inset_axes.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/inset_axes.rs b/src/inset_axes.rs index e920f47..714377e 100644 --- a/src/inset_axes.rs +++ b/src/inset_axes.rs @@ -388,19 +388,18 @@ mod tests { assert!(buffer.contains("plt.gca().indicate_inset_zoom(zoom")); } - #[test] #[test] fn test_indicator_disabled() { let mut inset = InsetAxes::new(); - assert!(!inset.indicator_disabled); - + assert_eq!(inset.indicator_disabled, false); + inset.set_indicator_disabled(true); - assert!(inset.indicator_disabled); - + assert_eq!(inset.indicator_disabled, true); + inset.draw(0.5, 0.5, 0.4, 0.3); let buffer = inset.get_buffer(); assert!(!buffer.contains("indicate_inset_zoom")); - + inset.set_indicator_disabled(false); inset.clear_buffer(); inset.draw(0.5, 0.5, 0.4, 0.3); From 42ec0a560e99d31d09c990d45628d78796990b04 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 10 Feb 2025 08:04:19 +1000 Subject: [PATCH 4/7] Add test --- tests/test_inset_axes.rs | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/test_inset_axes.rs b/tests/test_inset_axes.rs index b6cc590..6ec065f 100644 --- a/tests/test_inset_axes.rs +++ b/tests/test_inset_axes.rs @@ -252,3 +252,55 @@ fn test_inset_axes_6() -> Result<(), StrError> { assert!(n > 920 && n < 1010); Ok(()) } + +#[test] +fn test_inset_axes_7() -> Result<(), StrError> { + // curve + let mut curve = Curve::new(); + let x = &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]; + let y = &[1.0, 4.0, 9.0, 16.0, 25.0, 36.0, 49.0, 64.0]; + curve.draw(x, y); + + // first inset axes + let mut inset1 = InsetAxes::new(); + inset1 + .set_indicator_line_color("red") + .add(&curve) + .set_range(7.0, 8.2, 55.0, 65.0) + .draw(0.02, 0.78, 0.2, 0.2); + + // second inset axes + let mut inset2 = InsetAxes::new(); + inset2 + .set_indicator_line_color("green") + .add(&curve) + .set_range(0.8, 2.5, 0.0, 13.0) + .draw(0.02, 0.38, 0.3, 0.2); + + // third inset axes + let mut inset3 = InsetAxes::new(); + inset3 + .set_indicator_disabled(true) + .add(&curve) + .draw(0.6, 0.02, 0.38, 0.38); + + // add to plot + let mut plot = Plot::new(); + plot.add(&curve); + + // save figure + let path = Path::new(OUT_DIR).join("integ_inset_axes_7.svg"); + plot.add(&inset1) + .add(&inset2) + .add(&inset3) + .set_show_errors(true) + .save(&path)?; + + // check number of lines + let file = File::open(path).map_err(|_| "cannot open file")?; + let buffered = BufReader::new(file); + let lines_iter = buffered.lines(); + let n = lines_iter.count().clone(); + // assert!(n > 500 && n < 600); + Ok(()) +} From 1582df05275bba2de088ad2f0bb6f95d032add61 Mon Sep 17 00:00:00 2001 From: "Dorival Pedroso (aider)" Date: Mon, 10 Feb 2025 08:07:12 +1000 Subject: [PATCH 5/7] refactor: Combine axis range fields into single Option<(f64, f64, f64, f64)> tuple --- src/inset_axes.rs | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/inset_axes.rs b/src/inset_axes.rs index 714377e..5fb003b 100644 --- a/src/inset_axes.rs +++ b/src/inset_axes.rs @@ -37,10 +37,7 @@ use std::fmt::Write; /// then the inset must be added after the range has been set. Otherwise, the inset will not be displayed correctly. /// Specifically the connector lines will not be drawn if the inset is added before `set_range`. pub struct InsetAxes { - xmin: f64, - xmax: f64, - ymin: f64, - ymax: f64, + range: Option<(f64, f64, f64, f64)>, extra_for_axes: String, extra_for_indicator: String, indicator_line_style: String, @@ -78,10 +75,7 @@ impl InsetAxes { /// ``` pub fn new() -> Self { Self { - xmin: 0.0, - xmax: 1.0, - ymin: 0.0, - ymax: 1.0, + range: None, extra_for_axes: String::new(), extra_for_indicator: String::new(), indicator_line_style: String::new(), @@ -202,8 +196,12 @@ impl InsetAxes { self.buffer.insert_str( 0, &format!( - "zoom=plt.gca().inset_axes([{},{},{},{}],xlim=({},{}),ylim=({},{}){})\n", - u0, v0, width, height, self.xmin, self.xmax, self.ymin, self.ymax, opt1, + "zoom=plt.gca().inset_axes([{},{},{},{}]{}{})\n", + u0, v0, width, height, + self.range.map_or(String::new(), |(xmin, xmax, ymin, ymax)| { + format!(",xlim=({},{}),ylim=({},{})", xmin, xmax, ymin, ymax) + }), + opt1, ), ); if !self.axes_visible { @@ -219,10 +217,7 @@ impl InsetAxes { /// Sets the limits of axes in the inset. pub fn set_range(&mut self, xmin: f64, xmax: f64, ymin: f64, ymax: f64) -> &mut Self { - self.xmin = xmin; - self.xmax = xmax; - self.ymin = ymin; - self.ymax = ymax; + self.range = Some((xmin, xmax, ymin, ymax)); self } @@ -328,10 +323,7 @@ mod tests { #[test] fn test_new() { let inset = InsetAxes::new(); - assert_eq!(inset.xmin, 0.0); - assert_eq!(inset.xmax, 1.0); - assert_eq!(inset.ymin, 0.0); - assert_eq!(inset.ymax, 1.0); + assert_eq!(inset.range, None); assert!(inset.buffer.is_empty()); } @@ -339,10 +331,7 @@ mod tests { fn test_set_range() { let mut inset = InsetAxes::new(); inset.set_range(-1.0, 2.0, -3.0, 4.0); - assert_eq!(inset.xmin, -1.0); - assert_eq!(inset.xmax, 2.0); - assert_eq!(inset.ymin, -3.0); - assert_eq!(inset.ymax, 4.0); + assert_eq!(inset.range, Some((-1.0, 2.0, -3.0, 4.0))); } #[test] From 2e9362b4965144b80d6f197d7614d12ee04954f2 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 10 Feb 2025 08:16:30 +1000 Subject: [PATCH 6/7] Improve handling of optional range --- figures/integ_inset_axes_7.svg | 943 +++++++++++++++++++++++++++++++++ src/inset_axes.rs | 28 +- tests/test_inset_axes.rs | 6 +- 3 files changed, 964 insertions(+), 13 deletions(-) create mode 100644 figures/integ_inset_axes_7.svg diff --git a/figures/integ_inset_axes_7.svg b/figures/integ_inset_axes_7.svg new file mode 100644 index 0000000..a630008 --- /dev/null +++ b/figures/integ_inset_axes_7.svg @@ -0,0 +1,943 @@ + + + + + + + + 2025-02-10T08:15:09.489054 + image/svg+xml + + + Matplotlib v3.6.3, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/inset_axes.rs b/src/inset_axes.rs index 5fb003b..3e5cb89 100644 --- a/src/inset_axes.rs +++ b/src/inset_axes.rs @@ -193,17 +193,23 @@ impl InsetAxes { pub fn draw(&mut self, u0: f64, v0: f64, width: f64, height: f64) { let opt1 = self.options_for_axes(); let opt2 = self.options_for_indicator(); - self.buffer.insert_str( - 0, - &format!( - "zoom=plt.gca().inset_axes([{},{},{},{}]{}{})\n", - u0, v0, width, height, - self.range.map_or(String::new(), |(xmin, xmax, ymin, ymax)| { - format!(",xlim=({},{}),ylim=({},{})", xmin, xmax, ymin, ymax) - }), - opt1, - ), - ); + if let Some((xmin, xmax, ymin, ymax)) = self.range { + self.buffer.insert_str( + 0, + &format!( + "zoom=plt.gca().inset_axes([{},{},{},{}],xlim=({},{}),ylim=({},{}){})\n", + u0, v0, width, height, xmin, xmax, ymin, ymax, opt1, + ), + ); + } else { + self.buffer.insert_str( + 0, + &format!( + "zoom=plt.gca().inset_axes([{},{},{},{}]{})\n", + u0, v0, width, height, opt1, + ), + ); + } if !self.axes_visible { write!(&mut self.buffer, "zoom.set_xticks([])\nzoom.set_yticks([])\n").unwrap(); } diff --git a/tests/test_inset_axes.rs b/tests/test_inset_axes.rs index 6ec065f..4eeaf68 100644 --- a/tests/test_inset_axes.rs +++ b/tests/test_inset_axes.rs @@ -281,8 +281,10 @@ fn test_inset_axes_7() -> Result<(), StrError> { let mut inset3 = InsetAxes::new(); inset3 .set_indicator_disabled(true) + .set_visibility(true) + .set_extra_for_axes("title='MINIATURE'") .add(&curve) - .draw(0.6, 0.02, 0.38, 0.38); + .draw(0.64, 0.08, 0.34, 0.34); // add to plot let mut plot = Plot::new(); @@ -301,6 +303,6 @@ fn test_inset_axes_7() -> Result<(), StrError> { let buffered = BufReader::new(file); let lines_iter = buffered.lines(); let n = lines_iter.count().clone(); - // assert!(n > 500 && n < 600); + assert!(n > 900 && n < 1000); Ok(()) } From 3f16b7a64b4c7c86f2a2ef800c8a825ea12e782b Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 10 Feb 2025 08:18:15 +1000 Subject: [PATCH 7/7] Add example to Readme file --- examples/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/README.md b/examples/README.md index 5a9f872..03eea59 100644 --- a/examples/README.md +++ b/examples/README.md @@ -108,6 +108,7 @@ Some output of integration tests are shown below. ![inset_axes](https://raw.githubusercontent.com/cpmech/plotpy/main/figures/integ_inset_axes_4.svg) ![inset_axes](https://raw.githubusercontent.com/cpmech/plotpy/main/figures/integ_inset_axes_5.svg) ![inset_axes](https://raw.githubusercontent.com/cpmech/plotpy/main/figures/integ_inset_axes_6.svg) +![inset_axes](https://raw.githubusercontent.com/cpmech/plotpy/main/figures/integ_inset_axes_7.svg) ## Legend