diff --git a/.gitignore b/.gitignore index 3a262620e2261b63f17bb467b6c6a8c1ed71168d..ba17b3f1c88ffad882f148774a0dedc97820e893 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ target/ .project .settings/ *.png +*.jpg CSV/ \ No newline at end of file diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart03.java b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart03.java index 5dc34019aa4f7bf39bb258dfe6a5d11dae092759..80f1e8e351fb8722bf50cafc0e3f455d2a4be784 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart03.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart03.java @@ -83,9 +83,13 @@ public class LineChart03 implements ExampleChart { chart.getStyleManager().setChartTitleBoxVisible(true); chart.getStyleManager().setChartTitleBoxBorderColor(Color.BLACK); chart.getStyleManager().setPlotGridLinesVisible(false); + chart.getStyleManager().setAxisTickPadding(20); + chart.getStyleManager().setAxisTickMarkLength(15); + chart.getStyleManager().setPlotPadding(20); + chart.getStyleManager().setChartTitleFont(new Font(Font.MONOSPACED, Font.BOLD, 24)); chart.getStyleManager().setLegendFont(new Font(Font.SERIF, Font.PLAIN, 18)); chart.getStyleManager().setLegendPosition(LegendPosition.InsideSE); diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/standalone/Example1.java b/xchart-demo/src/main/java/com/xeiam/xchart/standalone/Example1.java index bb791b577ec1b9f1b8e4f0b97ea123f88ed6518f..75dbf9e08e471987e7a54e2a5ae6950caf060ab5 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/standalone/Example1.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/standalone/Example1.java @@ -35,6 +35,7 @@ public class Example1 { chart.addSeries("y(x)", null, yData); BitmapEncoder.savePNG(chart, "./Sample_Chart.png"); + BitmapEncoder.savePNGWithDPI(chart, "./Sample_Chart_300_DPI.png", 300); BitmapEncoder.saveJPG(chart, "./Sample_Chart.jpg", 0.95f); } diff --git a/xchart/src/main/java/com/xeiam/xchart/BitmapEncoder.java b/xchart/src/main/java/com/xeiam/xchart/BitmapEncoder.java index 9d148eff75a461e383065b50c5051be190919f19..964abf2fdd3207c03e25cb637b1b0178599ae4e2 100644 --- a/xchart/src/main/java/com/xeiam/xchart/BitmapEncoder.java +++ b/xchart/src/main/java/com/xeiam/xchart/BitmapEncoder.java @@ -16,6 +16,7 @@ package com.xeiam.xchart; import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileNotFoundException; @@ -26,8 +27,12 @@ import java.util.Iterator; import javax.imageio.IIOImage; import javax.imageio.ImageIO; +import javax.imageio.ImageTypeSpecifier; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; +import javax.imageio.metadata.IIOInvalidTreeException; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.metadata.IIOMetadataNode; import javax.imageio.stream.FileImageOutputStream; /** @@ -63,6 +68,79 @@ public final class BitmapEncoder { out.close(); } + /** + * Save a chart as a PNG with a custom DPI. The default DPI is 72, which is fine for displaying charts on a computer monitor, but for printing charts, a DPI of around 300 is much better. + * + * @param chart + * @param fileName + * @param DPI + * @throws IOException + */ + public static void savePNGWithDPI(Chart chart, String fileName, int DPI) throws IOException { + + double scaleFactor = DPI / 72.0; + + BufferedImage bufferedImage = new BufferedImage((int) (chart.getWidth() * scaleFactor), (int) (chart.getHeight() * scaleFactor), BufferedImage.TYPE_INT_RGB); + + Graphics2D graphics2D = bufferedImage.createGraphics(); + + AffineTransform at = graphics2D.getTransform(); + at.scale(scaleFactor, scaleFactor); + graphics2D.setTransform(at); + + chart.paint(graphics2D, chart.getWidth(), chart.getHeight()); + + for (Iterator<ImageWriter> iw = ImageIO.getImageWritersByFormatName("png"); iw.hasNext();) { + ImageWriter writer = iw.next(); + // instantiate an ImageWriteParam object with default compression options + ImageWriteParam iwp = writer.getDefaultWriteParam(); + + ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB); + IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, iwp); + if (metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported()) { + continue; + } + + setDPIforPNG(metadata, DPI); + + File file = new File(fileName); + FileImageOutputStream output = new FileImageOutputStream(file); + writer.setOutput(output); + IIOImage image = new IIOImage(bufferedImage, null, metadata); + writer.write(null, image, iwp); + writer.dispose(); + break; + } + } + + /** + * Sets the metadata correctly + * + * @param metadata + * @param DPI + * @throws IIOInvalidTreeException + */ + private static void setDPIforPNG(IIOMetadata metadata, int DPI) throws IIOInvalidTreeException { + + // for PNG, it's dots per millimeter + double dotsPerMilli = 1.0 * DPI / 10 / 2.54; + + IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize"); + horiz.setAttribute("value", Double.toString(dotsPerMilli)); + + IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize"); + vert.setAttribute("value", Double.toString(dotsPerMilli)); + + IIOMetadataNode dim = new IIOMetadataNode("Dimension"); + dim.appendChild(horiz); + dim.appendChild(vert); + + IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0"); + root.appendChild(dim); + + metadata.mergeTree("javax_imageio_1.0", root); + } + /** * Save a Chart as a JPEG file * @@ -92,4 +170,12 @@ public final class BitmapEncoder { writer.dispose(); } + public static void main(String[] args) { + + for (String format : ImageIO.getWriterFormatNames()) { + System.out.println(format); + // ImageIO.write(bufferedImage, format, new File("C:\\image_new." + format)); + } + } + } diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java index 15b44ee2bafa7b45b73997ab29bbeb46afe57fd1..4d156ed827d89f561c772b549b7ae5182c0db42c 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java @@ -19,6 +19,7 @@ import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; +import java.awt.geom.Rectangle2D; import java.math.BigDecimal; import com.xeiam.xchart.StyleManager.LegendPosition; @@ -128,7 +129,7 @@ public class Axis implements ChartPart { double titleHeight = 0.0; if (axisTitle.getText() != null && !axisTitle.getText().trim().equalsIgnoreCase("") && getChartPainter().getStyleManager().isXAxisTitleVisible()) { TextLayout textLayout = new TextLayout(axisTitle.getText(), getChartPainter().getStyleManager().getAxisTitleFont(), new FontRenderContext(null, true, false)); - Rectangle rectangle = textLayout.getPixelBounds(null, 0, 0); + Rectangle2D rectangle = textLayout.getBounds(); titleHeight = rectangle.getHeight() + getChartPainter().getStyleManager().getAxisTitlePadding(); } @@ -136,9 +137,8 @@ public class Axis implements ChartPart { double axisTickLabelsHeight = 0.0; if (getChartPainter().getStyleManager().isXAxisTicksVisible()) { TextLayout textLayout = new TextLayout("0", getChartPainter().getStyleManager().getAxisTickLabelsFont(), new FontRenderContext(null, true, false)); - Rectangle rectangle = textLayout.getPixelBounds(null, 0, 0); - axisTickLabelsHeight = rectangle.getHeight() + getChartPainter().getStyleManager().getAxisTickPadding() + getChartPainter().getStyleManager().getAxisTickMarkLength() - + getChartPainter().getStyleManager().getPlotPadding(); + Rectangle2D rectangle = textLayout.getBounds(); + axisTickLabelsHeight = rectangle.getHeight() + getChartPainter().getStyleManager().getAxisTickPadding() + getChartPainter().getStyleManager().getAxisTickMarkLength(); } return (int) (titleHeight + axisTickLabelsHeight); } else { // Y-Axis @@ -165,7 +165,9 @@ public class Axis implements ChartPart { int xOffset = getChartPainter().getStyleManager().getChartPadding(); int yOffset = getChartPainter().getChartTitle().getSizeHint(); int width = 80; // arbitrary, final width depends on Axis tick labels - int height = getChartPainter().getHeight() - yOffset - axisPair.getxAxis().getSizeHint() - getChartPainter().getStyleManager().getChartPadding(); + + int height = getChartPainter().getHeight() - yOffset - axisPair.getxAxis().getSizeHint() - getChartPainter().getStyleManager().getPlotPadding() + - getChartPainter().getStyleManager().getChartPadding(); Rectangle yAxisRectangle = new Rectangle(xOffset, yOffset, width, height); this.paintZone = yAxisRectangle; // g.setColor(Color.green); @@ -190,9 +192,9 @@ public class Axis implements ChartPart { int xOffset = (int) (axisPair.getyAxis().getBounds().getWidth() + (getChartPainter().getStyleManager().isYAxisTicksVisible() ? getChartPainter().getStyleManager().getPlotPadding() : 0) + getChartPainter() .getStyleManager().getChartPadding()); - int yOffset = (int) (axisPair.getyAxis().getBounds().getY() + axisPair.getyAxis().getBounds().getHeight()); + int yOffset = (int) (axisPair.getyAxis().getBounds().getY() + axisPair.getyAxis().getBounds().getHeight() + getChartPainter().getStyleManager().getPlotPadding()); - int chartLegendWidth = 0; + double chartLegendWidth = 0; if (getChartPainter().getStyleManager().getLegendPosition() == LegendPosition.OutsideE) { chartLegendWidth = getChartPainter().getChartLegend().getSizeHint()[0]; } diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickLabels.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickLabels.java index 5edf008c8987bed54cb0514403c04f3d50e72602..52285376ef3dbdee39a2569ca5a48d109e734d98 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickLabels.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickLabels.java @@ -19,6 +19,7 @@ import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; +import java.awt.geom.Rectangle2D; /** * Axis tick labels @@ -69,7 +70,7 @@ public class AxisTickLabels implements ChartPart { FontRenderContext frc = g.getFontRenderContext(); // TextLayout layout = new TextLayout(tickLabel, font, new FontRenderContext(null, true, false)); TextLayout layout = new TextLayout(tickLabel, getChartPainter().getStyleManager().getAxisTickLabelsFont(), frc); - Rectangle tickLabelBounds = layout.getPixelBounds(null, 0, 0); + Rectangle2D tickLabelBounds = layout.getBounds(); layout.draw(g, xOffset, (int) (yOffset + axisTick.getAxis().getPaintZone().getHeight() - tickLocation + tickLabelBounds.getHeight() / 2.0)); if (tickLabelBounds.getWidth() > maxTickLabelWidth) { @@ -96,7 +97,7 @@ public class AxisTickLabels implements ChartPart { if (tickLabel != null) { // some are null for logarithmic axes FontRenderContext frc = g.getFontRenderContext(); TextLayout layout = new TextLayout(tickLabel, getChartPainter().getStyleManager().getAxisTickLabelsFont(), frc); - Rectangle tickLabelBounds = layout.getPixelBounds(null, 0, 0); + Rectangle2D tickLabelBounds = layout.getBounds(); layout.draw(g, (int) (xOffset + tickLocation - tickLabelBounds.getWidth() / 2.0), yOffset); if (tickLabelBounds.getHeight() > maxTickLabelHeight) { diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTitle.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTitle.java index abf855dac0fd27e404fede7829cfe6f9f2c87774..5c6985fb48af4b68e78432adcef4a4498a7dd21c 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTitle.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTitle.java @@ -20,6 +20,7 @@ import java.awt.Rectangle; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; /** * AxisTitle @@ -65,31 +66,19 @@ public class AxisTitle implements ChartPart { FontRenderContext frc = g.getFontRenderContext(); TextLayout nonRotatedTextLayout = new TextLayout(text, getChartPainter().getStyleManager().getAxisTitleFont(), frc); - Rectangle nonRotatedRectangle = nonRotatedTextLayout.getPixelBounds(null, 0, 0); - // System.out.println(nonRotatedRectangle); + Rectangle2D nonRotatedRectangle = nonRotatedTextLayout.getBounds(); // /////////////////////////////////////////////// - // AffineTransform at = new AffineTransform(); - // // Tx.translate(anchorx, anchory); // S3: final translation - // double theta = Math.PI / -2.0; - // at.rotate(theta); // S2: rotate around anchor - // // Tx.translate(-anchorx, -anchory); // S1: translate anchor to origin - // Font derivedFont = font.deriveFont(at); - // TextLayout rotatedTextLayout = new TextLayout(text, derivedFont, frc); - // // TextLayout rotatedTextLayout = new TextLayout(text, font.deriveFont(AffineTransform.getRotateInstance(Math.PI / -2.0, 0, 0)), frc); - // // Rectangle rotatedRectangle = rotatedTextLayout.getPixelBounds(null, 0, 0); - // // System.out.println(rotatedRectangle); - // int xOffset = (int) (axis.getPaintZone().getX() + nonRotatedRectangle.getHeight()); int yOffset = (int) ((axis.getPaintZone().getHeight() + nonRotatedRectangle.getWidth()) / 2.0 + axis.getPaintZone().getY()); AffineTransform orig = g.getTransform(); g.transform(AffineTransform.getRotateInstance(Math.PI / -2.0, xOffset, yOffset)); g.drawString(text, xOffset, yOffset); - // rotatedTextLayout.draw(g, xOffset, yOffset); // /////////////////////////////////////////////// g.setTransform(orig); + // System.out.println(nonRotatedRectangle.getHeight()); // bounds bounds = new Rectangle((int) (xOffset - nonRotatedRectangle.getHeight()), (int) (yOffset - nonRotatedRectangle.getWidth()), (int) nonRotatedRectangle.getHeight() @@ -106,7 +95,7 @@ public class AxisTitle implements ChartPart { FontRenderContext frc = g.getFontRenderContext(); TextLayout textLayout = new TextLayout(text, getChartPainter().getStyleManager().getAxisTitleFont(), frc); - Rectangle rectangle = textLayout.getPixelBounds(null, 0, 0); + Rectangle2D rectangle = textLayout.getBounds(); // System.out.println(rectangle); int xOffset = (int) (axis.getPaintZone().getX() + (axis.getPaintZone().getWidth() - rectangle.getWidth()) / 2.0); diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/ChartTitle.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/ChartTitle.java index 395e47aed3eb2fb37be0a4056889e872c8bad504..a840a5bc2eda868ecaeed032991b7b9885572b51 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/ChartTitle.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/ChartTitle.java @@ -20,6 +20,7 @@ import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; +import java.awt.geom.Rectangle2D; /** * Chart Title @@ -70,7 +71,7 @@ public class ChartTitle implements ChartPart { if (chartPainter.getStyleManager().isChartTitleVisible()) { TextLayout textLayout = new TextLayout(text, chartPainter.getStyleManager().getChartTitleFont(), new FontRenderContext(null, true, false)); - Rectangle rectangle = textLayout.getPixelBounds(null, 0, 0); + Rectangle2D rectangle = textLayout.getBounds(); int titleHeight = (int) ((chartPainter.getStyleManager().isChartTitleVisible() ? rectangle.getHeight() : 0)); return chartPainter.getStyleManager().getChartPadding() + 2 * chartPainter.getStyleManager().getChartTitlePadding() + titleHeight; } else { @@ -89,7 +90,7 @@ public class ChartTitle implements ChartPart { // create rectangle first for sizing FontRenderContext frc = g.getFontRenderContext(); TextLayout textLayout = new TextLayout(text, chartPainter.getStyleManager().getChartTitleFont(), frc); - Rectangle rectangle = textLayout.getPixelBounds(null, 0, 0); + Rectangle2D rectangle = textLayout.getBounds(); int xOffset = (int) chartPainter.getPlot().getBounds().getX(); int yOffset = chartPainter.getStyleManager().getChartPadding(); @@ -111,7 +112,7 @@ public class ChartTitle implements ChartPart { xOffset = (int) (chartPainter.getPlot().getBounds().getX() + (chartPainter.getPlot().getBounds().getWidth() - rectangle.getWidth()) / 2.0); yOffset = (int) (chartPainter.getStyleManager().getChartPadding() - rectangle.getY() + chartPainter.getStyleManager().getChartTitlePadding()); - // bounds = new Rectangle(xOffset, yOffset + ((int) rectangle.getY()), (int) rectangle.getWidth(), (int) (rectangle.getHeight())); + bounds = new Rectangle(xOffset, yOffset + ((int) rectangle.getY()), (int) rectangle.getWidth(), (int) (rectangle.getHeight())); // g.setColor(Color.green); // g.draw(bounds); diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java index fffe7f621cedc2466c022f1a3f966d45a2af427c..847f08f6de696d7560235dc256d17cfa359b9f06 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java @@ -18,8 +18,12 @@ package com.xeiam.xchart.internal.chartpart; import java.awt.BasicStroke; import java.awt.Graphics2D; import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Shape; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; import java.util.Map; import com.xeiam.xchart.Series; @@ -55,51 +59,51 @@ public class Legend implements ChartPart { * * @return */ - protected int[] getSizeHint() { + protected double[] getSizeHint() { if (chartPainter.getStyleManager().isLegendVisible()) { Map<Integer, Series> seriesMap = chartPainter.getAxisPair().getSeriesMap(); // determine legend text content max width - int legendTextContentMaxWidth = 0; - int legendTextContentMaxHeight = 0; + double legendTextContentMaxWidth = 0; + double legendTextContentMaxHeight = 0; for (Integer seriesId : seriesMap.keySet()) { Series series = seriesMap.get(seriesId); TextLayout textLayout = new TextLayout(series.getName(), chartPainter.getStyleManager().getLegendFont(), new FontRenderContext(null, true, false)); - Rectangle rectangle = textLayout.getPixelBounds(null, 0, 0); + Rectangle2D rectangle = textLayout.getBounds(); // System.out.println(rectangle); if (rectangle.getWidth() > legendTextContentMaxWidth) { - legendTextContentMaxWidth = (int) rectangle.getWidth(); + legendTextContentMaxWidth = rectangle.getWidth(); } if (rectangle.getHeight() > legendTextContentMaxHeight) { - legendTextContentMaxHeight = (int) rectangle.getHeight(); + legendTextContentMaxHeight = rectangle.getHeight(); } } // determine legend content height - int maxContentHeight = 0; + double maxContentHeight = 0; if (getChartPainter().getStyleManager().getChartType() != ChartType.Bar) { maxContentHeight = Math.max(legendTextContentMaxHeight, Marker.SIZE); } else { maxContentHeight = Math.max(legendTextContentMaxHeight, BOX_SIZE); } - int legendContentHeight = maxContentHeight * seriesMap.size() + chartPainter.getStyleManager().getLegendPadding() * (seriesMap.size() - 1); + double legendContentHeight = maxContentHeight * seriesMap.size() + chartPainter.getStyleManager().getLegendPadding() * (seriesMap.size() - 1); // determine legend content width - int legendContentWidth = 0; + double legendContentWidth = 0; if (getChartPainter().getStyleManager().getChartType() != ChartType.Bar) { legendContentWidth = (int) (3.0 * Marker.SIZE + chartPainter.getStyleManager().getLegendPadding() + legendTextContentMaxWidth); } else { legendContentWidth = BOX_SIZE + chartPainter.getStyleManager().getLegendPadding() + legendTextContentMaxWidth; } // Legend Box - int legendBoxWidth = legendContentWidth + 2 * chartPainter.getStyleManager().getLegendPadding(); - int legendBoxHeight = legendContentHeight + 2 * chartPainter.getStyleManager().getLegendPadding(); - return new int[] { legendBoxWidth, legendBoxHeight, maxContentHeight }; + double legendBoxWidth = legendContentWidth + 2 * chartPainter.getStyleManager().getLegendPadding(); + double legendBoxHeight = legendContentHeight + 2 * chartPainter.getStyleManager().getLegendPadding(); + return new double[] { legendBoxWidth, legendBoxHeight, maxContentHeight }; } else { - return new int[] { 0, 0, 0 }; + return new double[] { 0, 0, 0 }; } } @@ -113,16 +117,19 @@ public class Legend implements ChartPart { Map<Integer, Series> seriesMap = chartPainter.getAxisPair().getSeriesMap(); - int legendBoxWidth = getSizeHint()[0]; - int legendBoxHeight = getSizeHint()[1]; - int maxContentHeight = getSizeHint()[2]; + double legendBoxWidth = getSizeHint()[0]; + double legendBoxHeight = getSizeHint()[1]; + double maxContentHeight = getSizeHint()[2]; + // System.out.println(legendBoxWidth); + // System.out.println(legendBoxHeight); + // System.out.println(maxContentHeight); // legend draw position int xOffset = 0; int yOffset = 0; switch (chartPainter.getStyleManager().getLegendPosition()) { case OutsideE: - xOffset = chartPainter.getWidth() - legendBoxWidth - chartPainter.getStyleManager().getChartPadding(); + xOffset = (int) (chartPainter.getWidth() - legendBoxWidth - chartPainter.getStyleManager().getChartPadding()); yOffset = (int) (chartPainter.getPlot().getBounds().getY() + (chartPainter.getPlot().getBounds().getHeight() - legendBoxHeight) / 2.0); break; case InsideNW: @@ -146,10 +153,11 @@ public class Legend implements ChartPart { break; } g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); - g.setColor(chartPainter.getStyleManager().getLegendBorderColor()); - g.drawRect(xOffset, yOffset, legendBoxWidth, legendBoxHeight); + Rectangle2D rect = new Rectangle2D.Double(xOffset + 1, yOffset + 1, (int) (legendBoxWidth - 2), (int) (legendBoxHeight - 2)); g.setColor(chartPainter.getStyleManager().getLegendBackgroundColor()); - g.fillRect(xOffset + 1, yOffset + 1, legendBoxWidth - 1, legendBoxHeight - 1); + g.fill(rect); + g.setColor(chartPainter.getStyleManager().getLegendBorderColor()); + g.draw(rect); // Draw legend content inside legend box int startx = xOffset + chartPainter.getStyleManager().getLegendPadding(); @@ -161,15 +169,18 @@ public class Legend implements ChartPart { // paint line if (getChartPainter().getStyleManager().getChartType() != ChartType.Scatter && series.getStroke() != null) { + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); g.setColor(series.getStrokeColor()); g.setStroke(series.getStroke()); - g.drawLine(startx, starty + (int) (maxContentHeight / 2.0), (int) (startx + Marker.SIZE * 3.0), starty + (int) (maxContentHeight / 2.0)); + Shape line = new Line2D.Double(startx, (int) (starty + maxContentHeight / 2.0), (startx + Marker.SIZE * 3.0), (starty + maxContentHeight / 2.0)); + g.draw(line); + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT); } // paint marker if (series.getMarker() != null) { g.setColor(series.getMarkerColor()); - series.getMarker().paint(g, (int) (startx + (Marker.SIZE * 1.5)), starty + (int) (maxContentHeight / 2.0)); + series.getMarker().paint(g, (int) (startx + (Marker.SIZE * 1.5)), (int) (starty + maxContentHeight / 2.0)); } } else { // paint little box @@ -182,19 +193,20 @@ public class Legend implements ChartPart { } // paint series name + g.setColor(chartPainter.getStyleManager().getChartFontColor()); TextLayout layout = new TextLayout(series.getName(), chartPainter.getStyleManager().getLegendFont(), new FontRenderContext(null, true, false)); if (getChartPainter().getStyleManager().getChartType() != ChartType.Bar) { layout.draw(g, (float) (startx + Marker.SIZE + (Marker.SIZE * 1.5) + chartPainter.getStyleManager().getLegendPadding()), starty - + (int) ((maxContentHeight + layout.getPixelBounds(null, 0, 0).getHeight()) / 2.0)); + + (int) ((maxContentHeight - 1 + layout.getBounds().getHeight()) / 2.0)); } else { - layout.draw(g, startx + BOX_SIZE + chartPainter.getStyleManager().getLegendPadding(), starty + (int) ((maxContentHeight + layout.getPixelBounds(null, 0, 0).getHeight()) / 2.0)); + layout.draw(g, startx + BOX_SIZE + chartPainter.getStyleManager().getLegendPadding(), starty + (int) ((maxContentHeight + layout.getBounds().getHeight()) / 2.0)); } - starty = starty + maxContentHeight + chartPainter.getStyleManager().getLegendPadding(); + starty = (int) (starty + maxContentHeight + chartPainter.getStyleManager().getLegendPadding()); } // bounds - bounds = new Rectangle(xOffset, yOffset, legendBoxWidth, legendBoxHeight); + bounds = new Rectangle(xOffset, yOffset, (int) (legendBoxWidth), (int) (legendBoxHeight)); // g.setColor(Color.blue); // g.draw(bounds); } diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/markers/Circle.java b/xchart/src/main/java/com/xeiam/xchart/internal/markers/Circle.java index b3d1950d3bb8b45aff3d563f8281b0151343075d..94df961e5139a4704c0ca1bda8cc52ecfe398112 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/markers/Circle.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/markers/Circle.java @@ -16,6 +16,9 @@ package com.xeiam.xchart.internal.markers; import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.Ellipse2D; /** * @author timmolter @@ -26,7 +29,10 @@ public class Circle extends Marker { public void paint(Graphics2D g, int xOffset, int yOffset) { g.setStroke(stroke); - g.fillOval(xOffset + Marker.X_OFFSET, yOffset + Marker.Y_OFFSET, Marker.SIZE, Marker.SIZE); + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); + Shape circle = new Ellipse2D.Double(xOffset + Marker.X_OFFSET, yOffset + Marker.Y_OFFSET, Marker.SIZE, Marker.SIZE); + g.fill(circle); + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT); } diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/markers/Diamond.java b/xchart/src/main/java/com/xeiam/xchart/internal/markers/Diamond.java index 13f43d0b96d8eb6a19c4f649972484e6e5efcbfe..4d2dac52e4a9d9de3e907f82e3a9b7f7a0caf8af 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/markers/Diamond.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/markers/Diamond.java @@ -17,6 +17,8 @@ package com.xeiam.xchart.internal.markers; import java.awt.Graphics2D; import java.awt.Polygon; +import java.awt.RenderingHints; +import java.awt.Shape; /** * @author timmolter @@ -36,16 +38,18 @@ public class Diamond extends Marker { int halfSize = (int) (Math.ceil((Marker.SIZE + 3) / 2.0)); x[0] = xOffset - halfSize + 0; x[1] = xOffset - halfSize + halfSize; - x[2] = xOffset - halfSize + Marker.SIZE + 3; + x[2] = (int) (xOffset - halfSize + Marker.SIZE + 3); x[3] = xOffset - halfSize + halfSize; y[0] = 1 + yOffset - halfSize + halfSize; - y[1] = 1 + yOffset - halfSize + Marker.SIZE + 3; + y[1] = (int) (1 + yOffset - halfSize + Marker.SIZE + 3); y[2] = 1 + yOffset - halfSize + halfSize; y[3] = 1 + yOffset - halfSize + 0; - Polygon diamond = new Polygon(x, y, n); - g.fillPolygon(diamond); + Shape diamond = new Polygon(x, y, n); + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); + g.fill(diamond); + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT); } diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/markers/Marker.java b/xchart/src/main/java/com/xeiam/xchart/internal/markers/Marker.java index da1b55f968c47e1532cf58473d981d80c62cf24b..d4e2832631961d6d5a3b9c5fb7c82ec13fb31bb4 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/markers/Marker.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/markers/Marker.java @@ -25,10 +25,10 @@ public abstract class Marker { protected BasicStroke stroke = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); - public static final int SIZE = 7; // make this an odd number! + public static final double SIZE = 8; - public static final int X_OFFSET = (int) (-1.0 * (SIZE / 2.0)); - public static final int Y_OFFSET = (int) (-1.0 * (SIZE / 2.0)); + public static final double X_OFFSET = (int) (-1.0 * (SIZE / 2.0)); + public static final double Y_OFFSET = (int) (-1.0 * (SIZE / 2.0)); public abstract void paint(Graphics2D g, int xOffset, int yOffset); } diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/markers/Square.java b/xchart/src/main/java/com/xeiam/xchart/internal/markers/Square.java index 02503bcaa86a414f21db8f91fbbe560b2ff1f270..7012ee24c6d10a1c85ab8553609bbddb5dce3134 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/markers/Square.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/markers/Square.java @@ -16,7 +16,9 @@ package com.xeiam.xchart.internal.markers; import java.awt.Graphics2D; -import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.Rectangle2D; /** * @author timmolter @@ -27,7 +29,10 @@ public class Square extends Marker { public void paint(Graphics2D g, int xOffset, int yOffset) { g.setStroke(stroke); - g.fill(new Rectangle(xOffset + Marker.X_OFFSET, yOffset + Marker.Y_OFFSET, Marker.SIZE, Marker.SIZE)); + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); + Shape square = new Rectangle2D.Double(xOffset + Marker.X_OFFSET, yOffset + Marker.Y_OFFSET, Marker.SIZE, Marker.SIZE); + g.fill(square); + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT); } diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/markers/TriangleDown.java b/xchart/src/main/java/com/xeiam/xchart/internal/markers/TriangleDown.java index 59325ad0dc430ab65dafc2957e1a7fb96291d1f4..8730551c092f046e5bccda9f828a8b7899364e22 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/markers/TriangleDown.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/markers/TriangleDown.java @@ -17,6 +17,8 @@ package com.xeiam.xchart.internal.markers; import java.awt.Graphics2D; import java.awt.Polygon; +import java.awt.RenderingHints; +import java.awt.Shape; /** * @author timmolter @@ -36,14 +38,16 @@ public class TriangleDown extends Marker { int halfSize = (int) (Math.ceil((Marker.SIZE + 1) / 2.0)); x[0] = xOffset - halfSize + 0; x[1] = xOffset - halfSize + halfSize; - x[2] = xOffset - halfSize + Marker.SIZE + 1; + x[2] = (int) (xOffset - halfSize + Marker.SIZE + 1); y[0] = 1 + yOffset - halfSize + 0; - y[1] = 1 + yOffset - halfSize + Marker.SIZE + 1; + y[1] = (int) (1 + yOffset - halfSize + Marker.SIZE + 1); y[2] = 1 + yOffset - halfSize + 0; - Polygon triangle = new Polygon(x, y, n); - g.fillPolygon(triangle); + Shape triangle = new Polygon(x, y, n); + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); + g.fill(triangle); + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT); } } diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/markers/TriangleUp.java b/xchart/src/main/java/com/xeiam/xchart/internal/markers/TriangleUp.java index 15ded2417ecd133bf21b43660d95bbf565c4cdb1..d8b48281fa86c8dd873cd685f1d6571c2c2fd91b 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/markers/TriangleUp.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/markers/TriangleUp.java @@ -17,6 +17,8 @@ package com.xeiam.xchart.internal.markers; import java.awt.Graphics2D; import java.awt.Polygon; +import java.awt.RenderingHints; +import java.awt.Shape; /** * @author timmolter @@ -35,15 +37,17 @@ public class TriangleUp extends Marker { // Make a triangle int halfSize = (int) (Math.ceil((Marker.SIZE + 1) / 2.0)); x[0] = xOffset - halfSize + 0; - x[1] = xOffset - halfSize + Marker.SIZE + 1; + x[1] = (int) (xOffset - halfSize + Marker.SIZE + 1); x[2] = xOffset - halfSize + halfSize; - y[0] = yOffset - halfSize + Marker.SIZE + 1; - y[1] = yOffset - halfSize + Marker.SIZE + 1; + y[0] = (int) (yOffset - halfSize + Marker.SIZE + 1); + y[1] = (int) (yOffset - halfSize + Marker.SIZE + 1); y[2] = yOffset - halfSize + 0; - Polygon triangle = new Polygon(x, y, n); - g.fillPolygon(triangle); + Shape triangle = new Polygon(x, y, n); + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); + g.fill(triangle); + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT); } }