Repository: knowm/XChart (java)

Issue:
Control axis margin width
I would like to display two charts below each other where the data is properly aligned. However, the charts are misaligned due to different Y axis label widths. It would be great to have a way to specify the width.

<img width="228" alt="Screen Shot 2019-06-12 at 11 24 36" src="https://user-images.githubusercontent.com/333139/59339695-f1f10180-8d04-11e9-8d50-671ad33a4251.png">


Pull request description:
Added yAxisLeftWidthHint 
Added yAxisLeftWidthHint. This fixes #325 

Use `chart.getStyler().setYAxisLeftWidthHint(yAxisLeftWidth)` to control left y axis width.
If the given width is bigger, padding is added to left side. If the given width is smaller it has no effect.
To get actual size of left y axis width use `chart.getYAxisLeftWidth()`

See TestForIssue325 for details.

Sample screenshot shows default behaviour (0), small width (15) and a big enough width (55) in each column:
![image](https://user-images.githubusercontent.com/6737748/60876348-6a83a900-a244-11e9-9ece-a386359ce60d.png)




Patch:
diff --git a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Axis.java b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Axis.java
index ad378744..8113300e 100644
--- a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Axis.java
+++ b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Axis.java
@@ -162,7 +162,7 @@ public class Axis<ST extends AxesChartStyler, S extends AxesChartSeries> impleme
           Math.max(
               leftYAxisBounds.getY() + leftYAxisBounds.getHeight(),
               rightYAxisBounds.getY() + rightYAxisBounds.getHeight());
-      double xOffset = leftYAxisBounds.getWidth() + axesChartStyler.getChartPadding();
+      double xOffset = leftYAxisBounds.getWidth() + leftYAxisBounds.getX();
       double yOffset =
           maxYAxisY
               + axesChartStyler.getPlotMargin()
@@ -179,7 +179,8 @@ public class Axis<ST extends AxesChartStyler, S extends AxesChartSeries> impleme
           chart.getWidth()
               - leftYAxisBounds.getWidth() // y-axis was already painted
               - rightYAxisBounds.getWidth() // y-axis was already painted
-              - 2 * axesChartStyler.getChartPadding()
+              - leftYAxisBounds.getX() // use left y-axis x instead of padding
+              - 1 * axesChartStyler.getChartPadding() // right y-axis padding
 
               // - tickMargin is included in left & right y axis bounds
 
diff --git a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/AxisPair.java b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/AxisPair.java
index fe67a0c9..7abc1c35 100644
--- a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/AxisPair.java
+++ b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/AxisPair.java
@@ -65,6 +65,36 @@ public class AxisPair<ST extends AxesChartStyler, S extends AxesChartSeries> imp
     int leftCount = 0;
     double leftStart = chartPadding;
 
+    int desiredLeftYAxisWidth = styler.getYAxisLeftWidthHint();
+    // calculate width first
+    if (desiredLeftYAxisWidth > 0) {
+      double widthEstimation = 0;
+      for (Entry<Integer, Axis<ST, S>> e : yAxisMap.entrySet()) {
+        Axis<ST, S> ya = e.getValue();
+        if (styler.getYAxisGroupPosistion(e.getKey()) == YAxisPosition.Right) {
+          continue;
+        }
+        ya.preparePaint();
+        Rectangle2D.Double bounds = (java.awt.geom.Rectangle2D.Double) ya.getBounds();
+        // add padding before axis
+        double width = bounds.getWidth();
+        widthEstimation += width;
+        leftCount++;
+      }
+
+      if (leftCount > 1) {
+        widthEstimation += (leftCount - 1) * paddingBetweenAxes;
+      }
+      widthEstimation += leftCount * tickMargin;
+
+      if (widthEstimation < desiredLeftYAxisWidth) {
+        leftStart = desiredLeftYAxisWidth - widthEstimation;
+      }
+      
+      leftCount = 0;
+    }
+    double leftStartFirst = leftStart;
+    
     for (Entry<Integer, Axis<ST, S>> e : yAxisMap.entrySet()) {
       Axis<ST, S> ya = e.getValue();
       if (styler.getYAxisGroupPosistion(e.getKey()) == YAxisPosition.Right) {
@@ -174,7 +204,7 @@ public class AxisPair<ST extends AxesChartStyler, S extends AxesChartSeries> imp
 
     // fill left & right bounds
     Rectangle2D.Double bounds = (java.awt.geom.Rectangle2D.Double) yAxis.getBounds();
-    leftYAxisBounds.x = chartPadding;
+    leftYAxisBounds.x = leftStartFirst;
     leftYAxisBounds.y = bounds.y;
     leftYAxisBounds.height = bounds.height;
 
diff --git a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Chart.java b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Chart.java
index 690f2eda..930d8513 100644
--- a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Chart.java
+++ b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Chart.java
@@ -283,4 +283,10 @@ public abstract class Chart<ST extends Styler, S extends Series> {
     }
     return axisPair.getYAxis(yIndex).getScreenValue(yValue);
   }
+  
+  public double getYAxisLeftWidth() {
+    
+    java.awt.geom.Rectangle2D.Double bounds = getAxisPair().getLeftYAxisBounds();
+    return  bounds.width + bounds.x;
+  }
 }
diff --git a/xchart/src/main/java/org/knowm/xchart/style/Styler.java b/xchart/src/main/java/org/knowm/xchart/style/Styler.java
index 5dbecb5b..4d699f37 100644
--- a/xchart/src/main/java/org/knowm/xchart/style/Styler.java
+++ b/xchart/src/main/java/org/knowm/xchart/style/Styler.java
@@ -59,6 +59,7 @@ public abstract class Styler {
   private boolean antiAlias = true;
   private String decimalPattern;
   private HashMap<Integer, YAxisPosition> yAxisAlignmentMap = new HashMap<Integer, YAxisPosition>();
+  private int yAxisLeftWidthHint;
 
   void setAllStyles() {
 
@@ -691,6 +692,16 @@ public abstract class Styler {
     antiAlias = newVal;
   }
 
+  public int getYAxisLeftWidthHint() {
+
+    return yAxisLeftWidthHint;
+  }
+
+  public void setYAxisLeftWidthHint(int yAxisLeftWidthHint) {
+
+    this.yAxisLeftWidthHint = yAxisLeftWidthHint;
+  }
+  
   public enum LegendPosition {
     OutsideE,
     InsideNW,


Test patch:
diff --git a/xchart-demo/src/main/java/org/knowm/xchart/standalone/issues/TestForIssue325.java b/xchart-demo/src/main/java/org/knowm/xchart/standalone/issues/TestForIssue325.java
new file mode 100644
index 00000000..45c8c470
--- /dev/null
+++ b/xchart-demo/src/main/java/org/knowm/xchart/standalone/issues/TestForIssue325.java
@@ -0,0 +1,60 @@
+package org.knowm.xchart.standalone.issues;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.knowm.xchart.SwingWrapper;
+import org.knowm.xchart.XYChart;
+import org.knowm.xchart.internal.chartpart.Chart;
+
+public class TestForIssue325 {
+
+  static final int WIDTH = 465;
+  static final int HEIGHT = 320;
+
+  static XYChart getChart(int multiple, int yAxisLeftWidth) {
+
+    int size = 30;
+    List<Integer> xData = new ArrayList<Integer>(size);
+    List<Double> yData = new ArrayList<Double>();
+    for (int i = 0; i <= size; i++) {
+      double radians = (Math.PI / (size / 2) * i) ;
+      xData.add(i * multiple);
+      yData.add(Math.sin(radians) * multiple);
+    }
+
+    XYChart chart = new XYChart(WIDTH, HEIGHT);
+    chart.addSeries("Series 0", xData, yData);
+
+    chart.getStyler().setLegendVisible(false);
+    chart.getStyler().setYAxisLeftWidthHint(yAxisLeftWidth);
+    return chart;
+  }
+
+  public static void main(String[] args) {
+
+    List<Chart> charts = new ArrayList<Chart>();
+    int[] multiples = { 1, 1000 };
+    int[] widths = { 0, 15, 55 };
+    for (int m : multiples) {
+      for (int width : widths) {
+        XYChart chart = getChart(m, width);
+        chart.setTitle("Multiple: " + m + " width: " + width);
+        charts.add(chart);
+      }
+    }
+
+    new SwingWrapper(charts, charts.size() / widths.length, widths.length).displayChartMatrix();
+    
+    try {
+      // wait frame to appear
+      Thread.sleep(500);
+    } catch (InterruptedException e) {
+      e.printStackTrace();
+    }
+    for (int i = 0; i < charts.size(); i++) {
+      System.out.printf("%-30s on screen width: %5.2f%n", charts.get(i).getTitle(),  charts.get(i).getYAxisLeftWidth());
+    }
+  }
+
+}