diff --git a/README.md b/README.md index a02919c14e..fea3fa941a 100644 --- a/README.md +++ b/README.md @@ -522,6 +522,7 @@ A number of classes have new methods. Nothing has been removed or deprecated: - 1400917 - OverLIBToolTipTagFragmentGenerator not escaping single quote; - 1363043 - Escape Image Map Data; - 1178601 - AbstractRenderer.hashcode() method returns the same value; +- cde9443 - related to open issue 215. Solve the scale problme for LogAxis when lower limit is 0. In addition, a bug in the constructor for the Week class has been fixed. diff --git a/XYSplineRenderer.java b/XYSplineRenderer.java new file mode 100644 index 0000000000..e68eadb75b --- /dev/null +++ b/XYSplineRenderer.java @@ -0,0 +1,511 @@ +/* =========================================================== + * JFreeChart : a free chart library for the Java(tm) platform + * =========================================================== + * + * (C) Copyright 2000-2021, by Object Refinery Limited and Contributors. + * + * Project Info: http://www.jfree.org/jfreechart/index.html + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners.] + * + * --------------------- + * XYSplineRenderer.java + * --------------------- + * (C) Copyright 2007-2021, by Klaus Rheinwald and Contributors. + * + * Original Author: Klaus Rheinwald; + * Contributor(s): Tobias von Petersdorff (tvp@math.umd.edu, + * http://www.wam.umd.edu/~petersd/); + * David Gilbert (for Object Refinery Limited); + * + */ + +package org.jfree.chart.renderer.xy; + +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import org.jfree.chart.axis.*; +import org.jfree.chart.event.RendererChangeEvent; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.PlotRenderingInfo; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.util.GradientPaintTransformer; +import org.jfree.chart.api.RectangleEdge; +import org.jfree.chart.util.StandardGradientPaintTransformer; +import org.jfree.chart.internal.Args; +import org.jfree.data.xy.XYDataset; + +/** + * A renderer that connects data points with natural cubic splines and/or + * draws shapes at each data point. This renderer is designed for use with + * the {@link XYPlot} class. The example shown here is generated by the + * {@code XYSplineRendererDemo1.java} program included in the JFreeChart + * demo collection: + *

+ * XYSplineRendererSample.png + * + * @since 1.0.7 + */ +public class XYSplineRenderer extends XYLineAndShapeRenderer { + + /** + * An enumeration of the fill types for the renderer. + * + * @since 1.0.17 + */ + public static enum FillType { + + /** No fill. */ + NONE, + + /** Fill down to zero. */ + TO_ZERO, + + /** Fill to the lower bound. */ + TO_LOWER_BOUND, + + /** Fill to the upper bound. */ + TO_UPPER_BOUND + } + + /** + * Represents state information that applies to a single rendering of + * a chart. + */ + public static class XYSplineState extends State { + + /** The area to fill under the curve. */ + public GeneralPath fillArea; + + /** The points. */ + public List points; + + /** + * Creates a new state instance. + * + * @param info the plot rendering info. + */ + public XYSplineState(PlotRenderingInfo info) { + super(info); + this.fillArea = new GeneralPath(); + this.points = new ArrayList<>(); + } + } + + /** + * Resolution of splines (number of line segments between points) + */ + private int precision; + + /** + * A flag that can be set to specify + * to fill the area under the spline. + */ + private FillType fillType; + + private GradientPaintTransformer gradientPaintTransformer; + + /** + * Creates a new instance with the precision attribute defaulting to 5 + * and no fill of the area 'under' the spline. + */ + public XYSplineRenderer() { + this(5, FillType.NONE); + } + + /** + * Creates a new renderer with the specified precision + * and no fill of the area 'under' (between '0' and) the spline. + * + * @param precision the number of points between data items. + */ + public XYSplineRenderer(int precision) { + this(precision, FillType.NONE); + } + + /** + * Creates a new renderer with the specified precision + * and specified fill of the area 'under' (between '0' and) the spline. + * + * @param precision the number of points between data items. + * @param fillType the type of fill beneath the curve ({@code null} + * not permitted). + * + * @since 1.0.17 + */ + public XYSplineRenderer(int precision, FillType fillType) { + super(); + if (precision <= 0) { + throw new IllegalArgumentException("Requires precision > 0."); + } + Args.nullNotPermitted(fillType, "fillType"); + this.precision = precision; + this.fillType = fillType; + this.gradientPaintTransformer = new StandardGradientPaintTransformer(); + } + + /** + * Returns the number of line segments used to approximate the spline + * curve between data points. + * + * @return The number of line segments. + * + * @see #setPrecision(int) + */ + public int getPrecision() { + return this.precision; + } + + /** + * Set the resolution of splines and sends a {@link RendererChangeEvent} + * to all registered listeners. + * + * @param p number of line segments between points (must be > 0). + * + * @see #getPrecision() + */ + public void setPrecision(int p) { + if (p <= 0) { + throw new IllegalArgumentException("Requires p > 0."); + } + this.precision = p; + fireChangeEvent(); + } + + /** + * Returns the type of fill that the renderer draws beneath the curve. + * + * @return The type of fill (never {@code null}). + * + * @see #setFillType(FillType) + * + * @since 1.0.17 + */ + public FillType getFillType() { + return this.fillType; + } + + /** + * Set the fill type and sends a {@link RendererChangeEvent} + * to all registered listeners. + * + * @param fillType the fill type ({@code null} not permitted). + * + * @see #getFillType() + * + * @since 1.0.17 + */ + public void setFillType(FillType fillType) { + this.fillType = fillType; + fireChangeEvent(); + } + + /** + * Returns the gradient paint transformer, or {@code null}. + * + * @return The gradient paint transformer (possibly {@code null}). + * + * @since 1.0.17 + */ + public GradientPaintTransformer getGradientPaintTransformer() { + return this.gradientPaintTransformer; + } + + /** + * Sets the gradient paint transformer and sends a + * {@link RendererChangeEvent} to all registered listeners. + * + * @param gpt the transformer ({@code null} permitted). + * + * @since 1.0.17 + */ + public void setGradientPaintTransformer(GradientPaintTransformer gpt) { + this.gradientPaintTransformer = gpt; + fireChangeEvent(); + } + + /** + * Initialises the renderer. + *

+ * This method will be called before the first item is rendered, giving the + * renderer an opportunity to initialise any state information it wants to + * maintain. The renderer can do nothing if it chooses. + * + * @param g2 the graphics device. + * @param dataArea the area inside the axes. + * @param plot the plot. + * @param data the data. + * @param info an optional info collection object to return data back to + * the caller. + * + * @return The renderer state. + */ + @Override + public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, + XYPlot plot, XYDataset data, PlotRenderingInfo info) { + + setDrawSeriesLineAsPath(true); + XYSplineState state = new XYSplineState(info); + state.setProcessVisibleItemsOnly(false); + return state; + } + + /** + * Draws the item (first pass). This method draws the lines + * connecting the items. Instead of drawing separate lines, + * a GeneralPath is constructed and drawn at the end of + * the series painting. + * + * @param g2 the graphics device. + * @param state the renderer state. + * @param plot the plot (can be used to obtain standard color information + * etc). + * @param dataset the dataset. + * @param pass the pass. + * @param series the series index (zero-based). + * @param item the item index (zero-based). + * @param xAxis the domain axis. + * @param yAxis the range axis. + * @param dataArea the area within which the data is being drawn. + */ + @Override + protected void drawPrimaryLineAsPath(XYItemRendererState state, + Graphics2D g2, XYPlot plot, XYDataset dataset, int pass, + int series, int item, ValueAxis xAxis, ValueAxis yAxis, + Rectangle2D dataArea) { + + XYSplineState s = (XYSplineState) state; + RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); + RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); + + // get the data points + double x1 = dataset.getXValue(series, item); + double y1 = dataset.getYValue(series, item); + double transX1 = xAxis.valueToJava2D(x1, dataArea, xAxisLocation); + double transY1 = yAxis.valueToJava2D(y1, dataArea, yAxisLocation); + + // Collect points + if (!Double.isNaN(transX1) && !Double.isNaN(transY1)) { + Point2D p = plot.getOrientation() == PlotOrientation.HORIZONTAL + ? new Point2D.Float((float) transY1, (float) transX1) + : new Point2D.Float((float) transX1, (float) transY1); + if (!s.points.contains(p)) + s.points.add(p); + } + + if (item == dataset.getItemCount(series) - 1) { // construct path + if (s.points.size() > 1) { + Point2D origin; + if (this.fillType == FillType.TO_ZERO) { + float xz = (float) xAxis.valueToJava2D(0, dataArea, + yAxisLocation); + float yz = (float) yAxis.valueToJava2D(0, dataArea, + yAxisLocation); + origin = plot.getOrientation() == PlotOrientation.HORIZONTAL + ? new Point2D.Float(yz, xz) + : new Point2D.Float(xz, yz); + } else if (this.fillType == FillType.TO_LOWER_BOUND) { + float xlb = (float) xAxis.valueToJava2D( + xAxis.getLowerBound(), dataArea, xAxisLocation); + float ylb = (float) yAxis.valueToJava2D( + yAxis.getLowerBound(), dataArea, yAxisLocation); + origin = plot.getOrientation() == PlotOrientation.HORIZONTAL + ? new Point2D.Float(ylb, xlb) + : new Point2D.Float(xlb, ylb); + } else {// fillType == TO_UPPER_BOUND + float xub = (float) xAxis.valueToJava2D( + xAxis.getUpperBound(), dataArea, xAxisLocation); + float yub = (float) yAxis.valueToJava2D( + yAxis.getUpperBound(), dataArea, yAxisLocation); + origin = plot.getOrientation() == PlotOrientation.HORIZONTAL + ? new Point2D.Float(yub, xub) + : new Point2D.Float(xub, yub); + } + + // we need at least two points to draw something + Point2D cp0 = s.points.get(0); + s.seriesPath.moveTo(cp0.getX(), cp0.getY()); + if (this.fillType != FillType.NONE) { + if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { + s.fillArea.moveTo(origin.getX(), cp0.getY()); + } else { + s.fillArea.moveTo(cp0.getX(), origin.getY()); + } + s.fillArea.lineTo(cp0.getX(), cp0.getY()); + } + if (s.points.size() == 2) { + // we need at least 3 points to spline. Draw simple line + // for two points + Point2D cp1 = s.points.get(1); + if (this.fillType != FillType.NONE) { + s.fillArea.lineTo(cp1.getX(), cp1.getY()); + s.fillArea.lineTo(cp1.getX(), origin.getY()); + s.fillArea.closePath(); + } + s.seriesPath.lineTo(cp1.getX(), cp1.getY()); + } else { + // construct spline + int np = s.points.size(); // number of points + float[] d = new float[np]; // Newton form coefficients + float[] x = new float[np]; // x-coordinates of nodes + float y, oldy; + float t, oldt; + + float[] a = new float[np]; + float t1; + float t2; + float[] h = new float[np]; + + for (int i = 0; i < np; i++) { + Point2D.Float cpi = (Point2D.Float) s.points.get(i); + x[i] = cpi.x; + d[i] = cpi.y; + } + + for (int i = 1; i <= np - 1; i++) + h[i] = x[i] - x[i - 1]; + + float[] sub = new float[np - 1]; + float[] diag = new float[np - 1]; + float[] sup = new float[np - 1]; + + for (int i = 1; i <= np - 2; i++) { + diag[i] = (h[i] + h[i + 1]) / 3; + sup[i] = h[i + 1] / 6; + sub[i] = h[i] / 6; + a[i] = (d[i + 1] - d[i]) / h[i + 1] + - (d[i] - d[i - 1]) / h[i]; + } + solveTridiag(sub, diag, sup, a, np - 2); + + // note that a[0]=a[np-1]=0 + oldt = x[0]; + oldy = d[0]; + for (int i = 1; i <= np - 1; i++) { + // loop over intervals between nodes + for (int j = 1; j <= this.precision; j++) { + t1 = (h[i] * j) / this.precision; + t2 = h[i] - t1; + y = ((-a[i - 1] / 6 * (t2 + h[i]) * t1 + d[i - 1]) + * t2 + (-a[i] / 6 * (t1 + h[i]) * t2 + + d[i]) * t1) / h[i]; + t = x[i - 1] + t1; + s.seriesPath.lineTo(t, y); + if (this.fillType != FillType.NONE) { + s.fillArea.lineTo(t, y); + } + } + } + } + // begin fill the path, the range of y should be set first + NumberAxis range = plot.getRangeAxisEdge(); + range.setRange(0, range.getUpperBound()); + + // Add last point @ y=0 for fillPath and close path + if (this.fillType != FillType.NONE) { + if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { + s.fillArea.lineTo(origin.getX(), s.points.get( + s.points.size() - 1).getY()); + } else { + s.fillArea.lineTo(s.points.get( + s.points.size() - 1).getX(), origin.getY()); + } + s.fillArea.closePath(); + } + + // fill under the curve... + if (this.fillType != FillType.NONE) { + Paint fp = getSeriesFillPaint(series); + if (this.gradientPaintTransformer != null + && fp instanceof GradientPaint) { + GradientPaint gp = this.gradientPaintTransformer + .transform((GradientPaint) fp, s.fillArea); + g2.setPaint(gp); + } else { + g2.setPaint(fp); + } + g2.fill(s.fillArea); + s.fillArea.reset(); + } + // then draw the line... + drawFirstPassShape(g2, pass, series, item, s.seriesPath); + } + // reset points vector + s.points = new ArrayList<>(); + } + } + + private void solveTridiag(float[] sub, float[] diag, float[] sup, + float[] b, int n) { +/* solve linear system with tridiagonal n by n matrix a + using Gaussian elimination *without* pivoting + where a(i,i-1) = sub[i] for 2<=i<=n + a(i,i) = diag[i] for 1<=i<=n + a(i,i+1) = sup[i] for 1<=i<=n-1 + (the values sub[1], sup[n] are ignored) + right hand side vector b[1:n] is overwritten with solution + NOTE: 1...n is used in all arrays, 0 is unused */ + int i; +/* factorization and forward substitution */ + for (i = 2; i <= n; i++) { + sub[i] /= diag[i - 1]; + diag[i] -= sub[i] * sup[i - 1]; + b[i] -= sub[i] * b[i - 1]; + } + b[n] /= diag[n]; + for (i = n - 1; i >= 1; i--) + b[i] = (b[i] - sup[i] * b[i + 1]) / diag[i]; + } + + /** + * Tests this renderer for equality with an arbitrary object. + * + * @param obj the object ({@code null} permitted). + * + * @return A boolean. + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof XYSplineRenderer)) { + return false; + } + XYSplineRenderer that = (XYSplineRenderer) obj; + if (this.precision != that.precision) { + return false; + } + if (this.fillType != that.fillType) { + return false; + } + if (!Objects.equals(this.gradientPaintTransformer, that.gradientPaintTransformer)) { + return false; + } + return super.equals(obj); + } +} diff --git a/src/main/java/org/jfree/chart/axis/LogAxis.java b/src/main/java/org/jfree/chart/axis/LogAxis.java index 57b545773f..b1e5cd6c34 100644 --- a/src/main/java/org/jfree/chart/axis/LogAxis.java +++ b/src/main/java/org/jfree/chart/axis/LogAxis.java @@ -871,6 +871,19 @@ protected double estimateMaximumTickLabelWidth(Graphics2D g2, return result; } + /** + * Set range for the log plot + * @param range the new range for the plot, adjusted by missing zero values + */ + @Override + public void setRange(Range range){ + super.setRange(range); + double lower = range.getLowerBound(); + if (lower < 0.0){ + setLowerBound(smallestValue); + } + } + /** * Zooms in on the current range. * diff --git a/src/main/java/org/jfree/chart/renderer/xy/XYSplineRenderer.java b/src/main/java/org/jfree/chart/renderer/xy/XYSplineRenderer.java index 64df374b7f..6c800b34f9 100644 --- a/src/main/java/org/jfree/chart/renderer/xy/XYSplineRenderer.java +++ b/src/main/java/org/jfree/chart/renderer/xy/XYSplineRenderer.java @@ -48,6 +48,7 @@ import java.util.List; import java.util.Objects; +import org.jfree.chart.axis.CyclicNumberAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.plot.PlotOrientation; @@ -421,6 +422,10 @@ protected void drawPrimaryLineAsPath(XYItemRendererState state, } } } + // begin fill the path, the range of y should be set first + CyclicNumberAxis range = plot.getRangeAxisEdge(); + range.setRange(0, range.getUpperBound()); + // Add last point @ y=0 for fillPath and close path if (this.fillType != FillType.NONE) { if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { diff --git a/src/test/java/org/jfree/chart/axis/LogAxisBoundTest.java b/src/test/java/org/jfree/chart/axis/LogAxisBoundTest.java new file mode 100644 index 0000000000..655327363b --- /dev/null +++ b/src/test/java/org/jfree/chart/axis/LogAxisBoundTest.java @@ -0,0 +1,192 @@ +/* =========================================================== + * JFreeChart : a free chart library for the Java(tm) platform + * =========================================================== + * + * (C) Copyright 2000-2020, by Object Refinery Limited and Contributors. + * + * Project Info: http://www.jfree.org/jfreechart/index.html + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners.] + * + * ---------------- + * LogAxisBoundTest.java + * ---------------- + * (C) Copyright 2007-2020, by Object Refinery Limited and Contributors. + * + * Original Author: David Gilbert (for Object Refinery Limited); + * Contributor(s): Zhiyue Xia created this test; + */ + +package org.jfree.chart.axis; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.TestUtils; +import org.jfree.chart.api.RectangleEdge; +import org.jfree.chart.plot.CategoryPlot; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.data.category.DefaultCategoryDataset; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; +import org.junit.jupiter.api.Test; + +import java.awt.*; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for the {@link LogAxis} class. + */ +public class LogAxisBoundTest { + + /** + * Confirm that the equals method can distinguish all the required fields. + */ + @Test + public void testEquals() { + LogAxis a1 = new LogAxis("Test"); + LogAxis a2 = new LogAxis("Test"); + assertTrue(a1.equals(a2)); + + a1.setBase(2.0); + assertFalse(a1.equals(a2)); + a2.setBase(2.0); + assertTrue(a1.equals(a2)); + + a1.setSmallestValue(0.1); + assertFalse(a1.equals(a2)); + a2.setSmallestValue(0.1); + assertTrue(a1.equals(a2)); + + a1.setMinorTickCount(8); + assertFalse(a1.equals(a2)); + a2.setMinorTickCount(8); + assertTrue(a1.equals(a2)); + } + + private static final double EPSILON = 0.0000001; + + /** + * Test the translation of Java2D values to data values. + */ + @Test + public void testTranslateJava2DToValue() { + LogAxis axis = new LogAxis(); + axis.setRange(50.0, 100.0); + Rectangle2D dataArea = new Rectangle2D.Double(10.0, 50.0, 400.0, 300.0); + double y1 = axis.java2DToValue(75.0, dataArea, RectangleEdge.LEFT); + assertEquals(94.3874312681693, y1, EPSILON); + double y2 = axis.java2DToValue(75.0, dataArea, RectangleEdge.RIGHT); + assertEquals(94.3874312681693, y2, EPSILON); + double x1 = axis.java2DToValue(75.0, dataArea, RectangleEdge.TOP); + assertEquals(55.961246381405, x1, EPSILON); + double x2 = axis.java2DToValue(75.0, dataArea, RectangleEdge.BOTTOM); + assertEquals(55.961246381405, x2, EPSILON); + axis.setInverted(true); + double y3 = axis.java2DToValue(75.0, dataArea, RectangleEdge.LEFT); + assertEquals(52.9731547179647, y3, EPSILON); + double y4 = axis.java2DToValue(75.0, dataArea, RectangleEdge.RIGHT); + assertEquals(52.9731547179647, y4, EPSILON); + double x3 = axis.java2DToValue(75.0, dataArea, RectangleEdge.TOP); + assertEquals(89.3475453695651, x3, EPSILON); + double x4 = axis.java2DToValue(75.0, dataArea, RectangleEdge.BOTTOM); + assertEquals(89.3475453695651, x4, EPSILON); + } + + + /** + * A simple test for the auto-range calculation looking at a + * LogAxis used as the range axis for a CategoryPlot. + */ + @Test + public void testAutoRange1() { + DefaultCategoryDataset dataset = new DefaultCategoryDataset<>(); + dataset.setValue(100.0, "Row 1", "Column 1"); + dataset.setValue(200.0, "Row 1", "Column 2"); + JFreeChart chart = ChartFactory.createBarChart("Test", "Categories", + "Value", dataset); + CategoryPlot plot = (CategoryPlot) chart.getPlot(); + LogAxis axis = new LogAxis("Log(Y)"); + plot.setRangeAxis(axis); + assertEquals(0.0, axis.getLowerBound(), EPSILON); + assertEquals(2.6066426411261268E7, axis.getUpperBound(), EPSILON); + } + + /** + * A simple test for the auto-range calculation looking at a + * NumberAxis used as the range axis for a CategoryPlot. In this + * case, the original dataset is replaced with a new dataset. + */ + @Test + public void testAutoRange3() { + DefaultCategoryDataset dataset = new DefaultCategoryDataset<>(); + dataset.setValue(100.0, "Row 1", "Column 1"); + dataset.setValue(200.0, "Row 1", "Column 2"); + JFreeChart chart = ChartFactory.createLineChart("Test", "Categories", + "Value", dataset, PlotOrientation.VERTICAL, false, false, + false); + @SuppressWarnings("unchecked") + CategoryPlot plot = (CategoryPlot) chart.getPlot(); + LogAxis axis = new LogAxis("Log(Y)"); + plot.setRangeAxis(axis); + assertEquals(96.59363289248458, axis.getLowerBound(), EPSILON); + assertEquals(207.0529847682752, axis.getUpperBound(), EPSILON); + + // now replacing the dataset should update the axis range... + DefaultCategoryDataset dataset2 = new DefaultCategoryDataset<>(); + dataset2.setValue(900.0, "Row 1", "Column 1"); + dataset2.setValue(1000.0, "Row 1", "Column 2"); + plot.setDataset(dataset2); + assertEquals(895.2712433374774, axis.getLowerBound(), EPSILON); + assertEquals(1005.2819262292991, axis.getUpperBound(), EPSILON); + } + + + /** + * Some checks for the setLowerBound() method. + */ + @Test + public void testSetLowerBound() { + LogAxis axis = new LogAxis("X"); + axis.setRange(0.0, 10.0); + axis.setLowerBound(0.0); + // set lower bound to a minimum number + assertEquals(1e-100, axis.getLowerBound(), EPSILON); + assertEquals(10.0, axis.getUpperBound(), EPSILON); + assertEquals(1.0E-100, axis.getSmallestValue(), EPSILON); + + } + + /** + * Checks that a TickUnit with a size of 0 doesn't crash. + */ + @Test + public void testRefreshTicksWithZeroTickUnit() { + LogAxis axis = new LogAxis(); + AxisState state = new AxisState(); + BufferedImage image = new BufferedImage(200, 100, + BufferedImage.TYPE_INT_ARGB); + Graphics2D g2 = image.createGraphics(); + Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, 200, 100); + axis.refreshTicks(g2, state, area, RectangleEdge.TOP); + } +}