Repository: knowm/XChart (java)

Issue:
using setXAxisMaxLabelCount against a HeatMapChart alters both axes
One would expect `setXAxisMaxLabelCount()` only alters the number of labels of the X axis and doesn't affect the number for Y axis.
Yet in HeatMapCharts that's not true.

e.g.
```
        int N = 12;
        java.util.List<Integer> xData = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
        java.util.List<String> yData =
                Arrays.asList("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December");
        java.util.List<Number[]> data = new ArrayList<>();
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                data.add(new Integer[]{i, j, (i * 2 + j * 3) % 7});
            }
        }

        HeatMapChart chart = new HeatMapChart(800, 600);
        chart.addSeries("only", xData, yData, data);
        chart.getStyler().setTheme(new XChartTheme());
        chart.getStyler().setXAxisMaxLabelCount(5); // <---------------- they key line!
        BitmapEncoder.saveBitmap(chart, "/tmp/chart.png", BitmapEncoder.BitmapFormat.PNG);
```
yields
![chart](https://user-images.githubusercontent.com/762437/107085367-b15acb80-67f8-11eb-9f51-0ffcedbe5909.png)


Pull request description:
setXAxisMaxLabelCount() not to affect Y Axis in HeatMapCharts
Fixes #564.

Note that the unit test code looks somewhat ugly due to it using reflection to get to private fields (axisPair, yAxis). An alternative would be to increase the visibility for these two members. I am not sure if a unit test is a good enough reason for that.

Patch:
diff --git a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/AxisTickCalculator_Category.java b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/AxisTickCalculator_Category.java
index 1c3e7952..92d96273 100644
--- a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/AxisTickCalculator_Category.java
+++ b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/AxisTickCalculator_Category.java
@@ -63,36 +63,38 @@ class AxisTickCalculator_Category extends AxisTickCalculator_ {
       throw new IllegalArgumentException("Unsupported max label count equal to 1");
     }
 
-    if (0 < xAxisMaxLabelCount && xAxisMaxLabelCount < categories.size()) {
-      List<Object> sparseCategories = new ArrayList<>();
-      double step = categories.size() / (double) (xAxisMaxLabelCount - 1);
-      for (double stepIdx = 0; Math.round(stepIdx) < categories.size(); stepIdx += step) {
-        int idx = (int) Math.round(stepIdx);
-        Object label = categories.get(idx);
-        sparseCategories.add(label);
+    if (this.axisDirection == Direction.X) {
+      if (0 < xAxisMaxLabelCount && xAxisMaxLabelCount < categories.size()) {
+        List<Object> sparseCategories = new ArrayList<>();
+        double step = categories.size() / (double) (xAxisMaxLabelCount - 1);
+        for (double stepIdx = 0; Math.round(stepIdx) < categories.size(); stepIdx += step) {
+          int idx = (int) Math.round(stepIdx);
+          Object label = categories.get(idx);
+          sparseCategories.add(label);
+        }
+
+        Object lastLabel = categories.get(categories.size() - 1);
+        sparseCategories.add(lastLabel);
+        categories = sparseCategories;
+
+        gridStep = (tickSpace / (categories.size() - 1));
+        firstPosition = 0;
       }
 
-      Object lastLabel = categories.get(categories.size() - 1);
-      sparseCategories.add(lastLabel);
-      categories = sparseCategories;
-
-      gridStep = (tickSpace / (categories.size() - 1));
-      firstPosition = 0;
-    }
-
-    // set up String formatters that may be encountered
-    if (axisType == Series.DataType.String) {
-      axisFormat = new Formatter_String();
-    } else if (axisType == Series.DataType.Number) {
-      axisFormat = new Formatter_Number(styler, axisDirection, minValue, maxValue);
-    } else if (axisType == Series.DataType.Date) {
-      if (styler.getDatePattern() == null) {
-        throw new RuntimeException("You need to set the Date Formatting Pattern!!!");
+      // set up String formatters that may be encountered
+      if (axisType == Series.DataType.String) {
+        axisFormat = new Formatter_String();
+      } else if (axisType == Series.DataType.Number) {
+        axisFormat = new Formatter_Number(styler, axisDirection, minValue, maxValue);
+      } else if (axisType == Series.DataType.Date) {
+        if (styler.getDatePattern() == null) {
+          throw new RuntimeException("You need to set the Date Formatting Pattern!!!");
+        }
+        SimpleDateFormat simpleDateformat =
+            new SimpleDateFormat(styler.getDatePattern(), styler.getLocale());
+        simpleDateformat.setTimeZone(styler.getTimezone());
+        axisFormat = simpleDateformat;
       }
-      SimpleDateFormat simpleDateformat =
-          new SimpleDateFormat(styler.getDatePattern(), styler.getLocale());
-      simpleDateformat.setTimeZone(styler.getTimezone());
-      axisFormat = simpleDateformat;
     }
 
     int counter = 0;


Test patch:
diff --git a/xchart/src/test/java/org/knowm/xchart/HeatMapChartTest.java b/xchart/src/test/java/org/knowm/xchart/HeatMapChartTest.java
new file mode 100644
index 00000000..ce74391c
--- /dev/null
+++ b/xchart/src/test/java/org/knowm/xchart/HeatMapChartTest.java
@@ -0,0 +1,76 @@
+package org.knowm.xchart;
+
+import java.io.ByteArrayOutputStream;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import org.junit.Assert;
+import org.junit.Test;
+import org.knowm.xchart.internal.chartpart.Axis;
+import org.knowm.xchart.internal.chartpart.AxisPair;
+import org.knowm.xchart.internal.chartpart.Chart;
+import org.knowm.xchart.style.theme.XChartTheme;
+
+public class HeatMapChartTest {
+
+  java.util.List<Integer> xData;
+  java.util.List<String> yData;
+  java.util.List<Number[]> data;
+
+  {
+    int N = 12;
+    xData = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
+    yData =
+        Arrays.asList(
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December");
+    data = new ArrayList<>();
+    for (int i = 0; i < N; i++) {
+      for (int j = 0; j < N; j++) {
+        data.add(new Integer[] {i, j, (i * 2 + j * 3) % 7});
+      }
+    }
+  }
+
+  @Test
+  public void shouldApplyMaxLabelCountOnlyToXAxis() throws Exception {
+    // given
+    HeatMapChart chart = new HeatMapChart(800, 600);
+    chart.addSeries("only", xData, yData, data);
+    chart.getStyler().setTheme(new XChartTheme());
+
+    // when
+    chart.getStyler().setXAxisMaxLabelCount(5);
+    BitmapEncoder.saveBitmap(chart, new ByteArrayOutputStream(), BitmapEncoder.BitmapFormat.PNG);
+
+    // test
+    Assert.assertEquals(5, getXAxis(chart).getAxisTickCalculator().getTickLocations().size());
+    Assert.assertEquals(12, getYAxis(chart).getAxisTickCalculator().getTickLocations().size());
+  }
+
+  private AxisPair<?, ?> getAxisPair(Chart<?, ?> chart) throws ReflectiveOperationException {
+    Field f = Chart.class.getDeclaredField("axisPair");
+    f.setAccessible(true);
+    return (AxisPair<?, ?>) f.get(chart);
+  }
+
+  public Axis<?, ?> getXAxis(Chart<?, ?> chart) throws ReflectiveOperationException {
+    return getAxisPair(chart).getXAxis();
+  }
+
+  public Axis<?, ?> getYAxis(Chart<?, ?> chart) throws ReflectiveOperationException {
+    Field f = AxisPair.class.getDeclaredField("yAxis");
+    f.setAccessible(true);
+    return (Axis<?, ?>) f.get(getAxisPair(chart));
+  }
+}