diff --git a/xchart-demo/src/main/java/org/knowm/xchart/standalone/issues/TestForIssue1024.java b/xchart-demo/src/main/java/org/knowm/xchart/standalone/issues/TestForIssue1024.java new file mode 100644 index 00000000..ed59ea1f --- /dev/null +++ b/xchart-demo/src/main/java/org/knowm/xchart/standalone/issues/TestForIssue1024.java @@ -0,0 +1,59 @@ +package org.knowm.xchart.standalone.issues; + +import java.util.Locale; +import org.knowm.xchart.SwingWrapper; +import org.knowm.xchart.XYChart; +import org.knowm.xchart.XYChartBuilder; +import org.knowm.xchart.style.Styler.LegendLayout; +import org.knowm.xchart.style.Styler.LegendPosition; +import org.knowm.xchart.style.colors.ChartColor; + +/** + * Demonstrates issue #1024 — after upgrading from 3.8.8 to 4.0.4 the x-axis line disappeared on + * charts with a {@code LegendPosition.OutsideS} legend. + * + *
{@code Axis_X.preparePaint()} subtracted the legend height from the top of the x-axis bounds + * even though the y-axis (whose bottom defines the plot bottom) had already reserved that space, so + * with an {@code OutsideS} legend the x-axis bounds started a legend-height inside the plot + * area. That was harmless while the axis line was positioned from the painted tick label geometry, + * but 4.0.4 anchors the line and tick marks to the top of the x-axis bounds — placing them under + * the plot, which is painted afterwards and covers them. The light grey plot background from the + * original report makes the missing black line obvious. + * + *
Run this and look below the plot: the x-axis line and tick marks sit plotMargin under the grey + * plot area, exactly as in 3.8.8. + */ +public class TestForIssue1024 { + + public static void main(String[] args) { + + new SwingWrapper<>(getChart()).displayChart(); + } + + /** Constructs and returns the chart without launching a window (headless-safe). */ + public static XYChart getChart() { + + XYChart chart = new XYChartBuilder().width(261).height(268).build(); + + // styler settings verbatim from the issue report + chart.getStyler().setPlotGridLinesColor(ChartColor.WHITE.getColor()); + + chart + .getStyler() + .setLocale(Locale.GERMANY) + .setChartTitleVisible(true) + .setPlotBorderVisible(false) + .setLegendBorderColor(null) + .setPlotBackgroundColor(ChartColor.LIGHT_GREY.getColor()) + .setChartBackgroundColor(ChartColor.WHITE.getColor()) + .setLegendPadding(5) + .setLegendPosition(LegendPosition.OutsideS) + .setLegendLayout(LegendLayout.Horizontal); + + // two points sloping from 2 down to 1, x around 30000 so the German locale renders the + // "30.000" tick label seen in the report's screenshots + chart.addSeries("0", new double[] {30000, 31000}, new double[] {2, 1}); + + return chart; + } +} diff --git a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Axis_X.java b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Axis_X.java index 3dd84be1..ccc93da0 100644 --- a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Axis_X.java +++ b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Axis_X.java @@ -61,7 +61,7 @@ public void preparePaint() { leftYAxisBounds.getY() + leftYAxisBounds.getHeight(), rightYAxisBounds.getY() + rightYAxisBounds.getHeight()); double xOffset = leftYAxisBounds.getWidth() + leftYAxisBounds.getX(); - double yOffset = maxYAxisY + axesChartStyler.getPlotMargin() - legendHeightOffset; + double yOffset = maxYAxisY + axesChartStyler.getPlotMargin(); double legendWidth = 0; if (axesChartStyler.getLegendPosition() == LegendPosition.OutsideE @@ -85,7 +85,8 @@ public void preparePaint() { chart.getHeight() - maxYAxisY - axesChartStyler.getChartPadding() - - axesChartStyler.getPlotMargin(); + - axesChartStyler.getPlotMargin() + - legendHeightOffset; bounds.setRect(xOffset, yOffset, width, height); } diff --git a/xchart/src/test/java/org/knowm/xchart/internal/chartpart/AxisLinePlotMarginTest.java b/xchart/src/test/java/org/knowm/xchart/internal/chartpart/AxisLinePlotMarginTest.java index e7a5d557..d6195e0c 100644 --- a/xchart/src/test/java/org/knowm/xchart/internal/chartpart/AxisLinePlotMarginTest.java +++ b/xchart/src/test/java/org/knowm/xchart/internal/chartpart/AxisLinePlotMarginTest.java @@ -8,6 +8,8 @@ import org.knowm.xchart.BitmapEncoder; import org.knowm.xchart.XYChart; import org.knowm.xchart.XYChartBuilder; +import org.knowm.xchart.style.Styler.LegendLayout; +import org.knowm.xchart.style.Styler.LegendPosition; // The axis tick lines that hug the plot area must sit at the same distance (plotMargin) from the // plot on both the y-axis and x-axis sides. Historically the y-axis line was positioned from the @@ -21,13 +23,38 @@ class AxisLinePlotMarginTest { @Test void axisLinesSitAtPlotMarginOnBothSides() throws Exception { + XYChart chart = buildChart(); + chart.getStyler().setLegendVisible(false); + + assertAxisLinesAtPlotMargin(chart); + } + + // Issue #1024: Axis_X.preparePaint() subtracted the OutsideS legend height from the top of the + // x-axis bounds even though the y-axis (whose bottom defines the plot bottom) had already + // reserved that space, so the x-axis line — anchored to that top — was drawn inside the plot + // area and painted over by the plot background. + @Test + void axisLinesSitAtPlotMarginWithOutsideSLegend() throws Exception { + + XYChart chart = buildChart(); + chart.getStyler().setLegendPosition(LegendPosition.OutsideS); + chart.getStyler().setLegendLayout(LegendLayout.Horizontal); + + assertAxisLinesAtPlotMargin(chart); + } + + private static XYChart buildChart() { + XYChart chart = new XYChartBuilder().width(800).height(600).title("t").build(); chart.addSeries("s", new double[] {1, 2, 3}, new double[] {10, 20, 30}); - chart.getStyler().setLegendVisible(false); chart.getStyler().setPlotGridLinesVisible(false); chart.getStyler().setPlotBackgroundColor(Color.YELLOW); chart.getStyler().setPlotBorderVisible(true); chart.getStyler().setPlotBorderColor(Color.RED); + return chart; + } + + private static void assertAxisLinesAtPlotMargin(XYChart chart) throws Exception { BufferedImage img = BitmapEncoder.getBufferedImage(chart);