Repository: plotters-rs/plotters (language: rust)

Issue:
[Feature Request] Minor gridline density
### What is the feature ?
Allow the user to specify the amount of light/minor grid lines

### Why this feature is useful and how people would use the feature ?
When creating a graph that is small or has many major grid lines close together, it may be ideal for the amount of minor grid lines to decrease to less than the currently hard coded, and quite aggressive 10.

### Additional Information
The implementation looks to have the amount of minor grid lines hard coded here: 
https://github.com/38/plotters/blob/master/src/chart/mesh.rs#L444-L445

#### Example
Ignoring the otherwise messed up formatting, the 10 minor grid lines basically join together to create a solid background, which is not what is wanted.

![image](https://user-images.githubusercontent.com/14093962/140622660-8855aa67-bc3b-4608-8b86-e048f9deefe3.png)



Discussion / hints:

Fix patch:
diff --git a/examples/3d-plot.rs b/examples/3d-plot.rs
index 87b43e09..af40cc29 100644
--- a/examples/3d-plot.rs
+++ b/examples/3d-plot.rs
@@ -18,7 +18,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
         pb.into_matrix()
     });
 
-    chart.configure_axes().draw()?;
+    chart
+        .configure_axes()
+        .light_grid_style(BLACK.mix(0.15))
+        .max_light_lines(3)
+        .draw()?;
 
     chart
         .draw_series(
diff --git a/examples/3d-plot2.rs b/examples/3d-plot2.rs
index d9461d0a..0b5ca213 100644
--- a/examples/3d-plot2.rs
+++ b/examples/3d-plot2.rs
@@ -24,7 +24,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
             p.into_matrix() // build the projection matrix
         });
 
-        chart.configure_axes().draw()?;
+        chart
+            .configure_axes()
+            .light_grid_style(BLACK.mix(0.15))
+            .max_light_lines(3)
+            .draw()?;
 
         chart.draw_series(
             SurfaceSeries::xoz(
diff --git a/examples/chart.rs b/examples/chart.rs
index 955add07..acdddc3c 100644
--- a/examples/chart.rs
+++ b/examples/chart.rs
@@ -68,7 +68,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
             .margin_right(20)
             .caption(format!("y = x^{}", 1 + 2 * idx), ("sans-serif", 40))
             .build_cartesian_2d(-1f32..1f32, -1f32..1f32)?;
-        cc.configure_mesh().x_labels(5).y_labels(3).draw()?;
+        cc.configure_mesh()
+            .x_labels(5)
+            .y_labels(3)
+            .max_light_lines(4)
+            .draw()?;
 
         cc.draw_series(LineSeries::new(
             (-1f32..1f32)
diff --git a/examples/matshow.rs b/examples/matshow.rs
index e3b48a45..30c71a74 100644
--- a/examples/matshow.rs
+++ b/examples/matshow.rs
@@ -17,6 +17,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
         .configure_mesh()
         .x_labels(15)
         .y_labels(15)
+        .max_light_lines(4)
         .x_label_offset(35)
         .y_label_offset(25)
         .disable_x_mesh()
diff --git a/examples/slc-temp.rs b/examples/slc-temp.rs
index f24c8d6f..9d6e4731 100644
--- a/examples/slc-temp.rs
+++ b/examples/slc-temp.rs
@@ -33,6 +33,7 @@ fn main() -> Result<(), Box<dyn Error>> {
         .disable_x_mesh()
         .disable_y_mesh()
         .x_labels(30)
+        .max_light_lines(4)
         .y_desc("Average Temp (F)")
         .draw()?;
     chart
diff --git a/src/chart/axes3d.rs b/src/chart/axes3d.rs
index 14dd29ba..e19a056f 100644
--- a/src/chart/axes3d.rs
+++ b/src/chart/axes3d.rs
@@ -18,6 +18,7 @@ pub struct Axes3dStyle<'a, 'b, X: Ranged, Y: Ranged, Z: Ranged, DB: DrawingBacke
     pub(super) parent_size: (u32, u32),
     pub(super) target: Option<&'b mut ChartContext<'a, DB, Cartesian3d<X, Y, Z>>>,
     pub(super) tick_size: i32,
+    pub(super) light_lines_limit: [usize; 3],
     pub(super) n_labels: [usize; 3],
     pub(super) bold_line_style: ShapeStyle,
     pub(super) light_line_style: ShapeStyle,
@@ -44,6 +45,36 @@ where
         self
     }
 
+    /// Set the maximum number of divisions for the minor grid
+    /// - `value`: Maximum desired divisions between two consecutive X labels
+    pub fn x_max_light_lines(&mut self, value: usize) -> &mut Self {
+        self.light_lines_limit[0] = value;
+        self
+    }
+
+    /// Set the maximum number of divisions for the minor grid
+    /// - `value`: Maximum desired divisions between two consecutive Y labels
+    pub fn y_max_light_lines(&mut self, value: usize) -> &mut Self {
+        self.light_lines_limit[1] = value;
+        self
+    }
+
+    /// Set the maximum number of divisions for the minor grid
+    /// - `value`: Maximum desired divisions between two consecutive Z labels
+    pub fn z_max_light_lines(&mut self, value: usize) -> &mut Self {
+        self.light_lines_limit[2] = value;
+        self
+    }
+
+    /// Set the maximum number of divisions for the minor grid
+    /// - `value`: Maximum desired divisions between two consecutive labels in X, Y, and Z
+    pub fn max_light_lines(&mut self, value: usize) -> &mut Self {
+        self.light_lines_limit[0] = value;
+        self.light_lines_limit[1] = value;
+        self.light_lines_limit[2] = value;
+        self
+    }
+
     /// Set the number of labels on the X axes
     pub fn x_labels(&mut self, n: usize) -> &mut Self {
         self.n_labels[0] = n;
@@ -104,6 +135,7 @@ where
         Self {
             parent_size,
             tick_size,
+            light_lines_limit: [10, 10, 10],
             n_labels: [10, 10, 10],
             bold_line_style: Into::<ShapeStyle>::into(&BLACK.mix(0.2)),
             light_line_style: Into::<ShapeStyle>::into(&TRANSPARENT),
@@ -130,9 +162,18 @@ where
             BoldPoints(self.n_labels[2]),
         );
         let kps_light = chart.get_key_points(
-            LightPoints::new(self.n_labels[0], self.n_labels[0] * 10),
-            LightPoints::new(self.n_labels[1], self.n_labels[1] * 10),
-            LightPoints::new(self.n_labels[2], self.n_labels[2] * 10),
+            LightPoints::new(
+                self.n_labels[0],
+                self.n_labels[0] * self.light_lines_limit[0],
+            ),
+            LightPoints::new(
+                self.n_labels[1],
+                self.n_labels[1] * self.light_lines_limit[1],
+            ),
+            LightPoints::new(
+                self.n_labels[2],
+                self.n_labels[2] * self.light_lines_limit[2],
+            ),
         );
 
         let panels = chart.draw_axis_panels(
diff --git a/src/chart/mesh.rs b/src/chart/mesh.rs
index c404223b..4a6d7444 100644
--- a/src/chart/mesh.rs
+++ b/src/chart/mesh.rs
@@ -149,6 +149,8 @@ pub struct MeshStyle<'a, 'b, X: Ranged, Y: Ranged, DB: DrawingBackend> {
     pub(super) draw_y_axis: bool,
     pub(super) x_label_offset: i32,
     pub(super) y_label_offset: i32,
+    pub(super) x_light_lines_limit: usize,
+    pub(super) y_light_lines_limit: usize,
     pub(super) n_x_labels: usize,
     pub(super) n_y_labels: usize,
     pub(super) axis_desc_style: Option<TextStyle<'b>>,
@@ -197,6 +199,8 @@ where
             draw_y_mesh: true,
             draw_x_axis: true,
             draw_y_axis: true,
+            x_light_lines_limit: 10,
+            y_light_lines_limit: 10,
             n_x_labels: 10,
             n_y_labels: 10,
             bold_line_style: None,
@@ -308,6 +312,29 @@ where
         self.axis_style = Some(style.into());
         self
     }
+
+    /// Set the maximum number of divisions for the minor grid
+    /// - `value`: Maximum desired divisions between two consecutive X labels
+    pub fn x_max_light_lines(&mut self, value: usize) -> &mut Self {
+        self.x_light_lines_limit = value;
+        self
+    }
+
+    /// Set the maximum number of divisions for the minor grid
+    /// - `value`: Maximum desired divisions between two consecutive Y labels
+    pub fn y_max_light_lines(&mut self, value: usize) -> &mut Self {
+        self.y_light_lines_limit = value;
+        self
+    }
+
+    /// Set the maximum number of divisions for the minor grid
+    /// - `value`: Maximum desired divisions between two consecutive labels in X and Y
+    pub fn max_light_lines(&mut self, value: usize) -> &mut Self {
+        self.x_light_lines_limit = value;
+        self.y_light_lines_limit = value;
+        self
+    }
+
     /// Set how many labels for the X axis at most
     /// - `value`: The maximum desired number of labels in the X axis
     pub fn x_labels(&mut self, value: usize) -> &mut Self {
@@ -441,8 +468,8 @@ where
 
         target.draw_mesh(
             (
-                LightPoints::new(self.n_y_labels, self.n_y_labels * 10),
-                LightPoints::new(self.n_x_labels, self.n_x_labels * 10),
+                LightPoints::new(self.n_y_labels, self.n_y_labels * self.y_light_lines_limit),
+                LightPoints::new(self.n_x_labels, self.n_x_labels * self.x_light_lines_limit),
             ),
             &light_style,
             &x_label_style,


Test patch: