diff --git a/.gitignore b/.gitignore index 078b81df2854d58eefa804f0f85f8f2d70bd3a07..2fbfc9c717b9e4cbfe87cb4115ed4c05e0084cb0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,32 @@ +# IntelliJ +.idea/ +*.iws +*.iml +*.ipr +*.ips +.idea/ + + +# Eclipse +.settings/ +.metadata/ +.classpath +.project + +# Generated target/ javadoc/ bin/ -.classpath -.project -.settings/ +log/ + +# Misc. .DS_Store + +# Sample images *.png *.jpg +*.bmp +*.gif +*.svg +*.pdf +*.eps \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 391b1a9b67ca6e519dec0eac57e36e4147541819..ee7f2f14b3ba656ae1a35a9c7b3123a0c21bf2c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,12 @@ language: java before_install: "git clone -b travis `git config --get remote.origin.url` target/travis" -script: "[ ${TRAVIS_PULL_REQUEST} = 'false' ] && mvn clean deploy --settings target/travis/settings.xml || mvn clean verify --settings target/travis/settings.xml" +script: " +if [ ${TRAVIS_PULL_REQUEST} = 'false' ]; +then + mvn -Dmaven.test.skip=true clean deploy --settings target/travis/settings.xml; +else + mvn clean verify --settings target/travis/settings.xml; +fi" # whitelist branches: diff --git a/README.md b/README.md index 839ab65f5bb227b611e925c0569e0471e18c15d5..5baa6a5f462396801c9d331bc1d30152ecfb0443 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## [](http://xeiam.com/xchart) XChart +## [](http://www.xeiam.com/xchart) XChart A Simple Charting Library for Java ## Description @@ -25,7 +25,7 @@ Usage is very simple: Create a Chart instance, add a series of data to it, and e // or save it in high-res BitmapEncoder.savePNGWithDPI(chart, "./Sample_Chart_300_DPI.png", 300); -Now go ahead and [study some more examples](http://xeiam.com/xchart_examplecode.jsp), [download the thing](http://xeiam.com/xchart_changelog.jsp) and [provide feedback](https://github.com/timmolter/XChart/issues). +Now go ahead and [study some more examples](http://www.xeiam.com/xchart_examplecode.jsp), [download the thing](http://www.xeiam.com/xchart_changelog.jsp) and [provide feedback](https://github.com/timmolter/XChart/issues). ## Features * No additional dependencies @@ -49,10 +49,13 @@ Now go ahead and [study some more examples](http://xeiam.com/xchart_examplecode. * Real-time charts ## Getting Started + ### Non-Maven -Download Jar: http://xeiam.com/xchart_changelog.jsp + +Download Jar: http://www.xeiam.com/xchart_changelog.jsp ### Maven + The XChart release artifacts are hosted on Maven Central. Add the XChart library as a dependency to your pom.xml file: @@ -60,7 +63,7 @@ Add the XChart library as a dependency to your pom.xml file: <dependency> <groupId>com.xeiam.xchart</groupId> <artifactId>xchart</artifactId> - <version>2.3.2</version> + <version>2.4.0</version> </dependency> For snapshots, add the following to your pom.xml file: @@ -74,7 +77,7 @@ For snapshots, add the following to your pom.xml file: <dependency> <groupId>com.xeiam</groupId> <artifactId>xchart</artifactId> - <version>2.3.3-SNAPSHOT</version> + <version>2.4.1-SNAPSHOT</version> </dependency> ## Building @@ -92,7 +95,7 @@ For snapshots, add the following to your pom.xml file: ## Running Demo cd /path/to/xchart-demo/jar/ - java -cp xchart-demo-2.3.2.jar:xchart-2.3.2.jar com.xeiam.xchart.demo.XChartDemo + java -cp xchart-demo-2.4.0.jar:xchart-2.4.0.jar com.xeiam.xchart.demo.XChartDemo ## Bugs Please report any bugs or submit feature requests to [XChart's Github issue tracker](https://github.com/timmolter/XChart/issues). @@ -104,3 +107,6 @@ Please report any bugs or submit feature requests to [XChart's Github issue trac ## Donations 1PrZHiJorAw7RQrjP9CJgtPuqr6fU65PKt +## Release Information + +We will announce new releases on our [Twitter page](https://twitter.com/Xeiam). \ No newline at end of file diff --git a/pom.xml b/pom.xml index 784fa47b7cc0a78e876deb4e87b0d99b8ec2824c..fab436d0850495978b0096f09ea61bc438da6d0e 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ <groupId>com.xeiam.xchart</groupId> <artifactId>xchart-parent</artifactId> - <version>2.3.3-SNAPSHOT</version> + <version>2.4.1-SNAPSHOT</version> <packaging>pom</packaging> <name>XChart Parent</name> <description>Basic Charts for Java Applications</description> @@ -66,6 +66,24 @@ <downloadUrl>https://oss.sonatype.org/content/groups/public/com/xeiam/xchart</downloadUrl> </distributionManagement> + <repositories> + <repository> + <id>erichseifert.de</id> + <url>http://mvn.erichseifert.de/maven2</url> + </repository> + </repositories> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>de.erichseifert.vectorgraphics2d</groupId> + <artifactId>VectorGraphics2D</artifactId> + <version>0.9.1</version> + <optional>true</optional> + </dependency> + </dependencies> + </dependencyManagement> + <dependencies> <dependency> <groupId>junit</groupId> @@ -123,12 +141,12 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-release-plugin</artifactId> - <version>2.4</version> + <version>2.5</version> <configuration> <autoversionsubmodules>true</autoversionsubmodules> </configuration> </plugin> - + <!-- for header in all .java files --> <plugin> <groupId>com.mycila.maven-license-plugin</groupId> @@ -159,7 +177,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-gpg-plugin</artifactId> - <version>1.4</version> + <version>1.5</version> <executions> <execution> <id>sign-artifacts</id> diff --git a/xchart-demo/CSV/CSVChartColumnsExport/series1.csv b/xchart-demo/CSV/CSVChartColumnsExport/series1.csv index 73e712d7e7ed294bfe9971a71df06996bb8019d5..fb66c5087f8db5e3d438ab186484a8b8f874ed6f 100644 --- a/xchart-demo/CSV/CSVChartColumnsExport/series1.csv +++ b/xchart-demo/CSV/CSVChartColumnsExport/series1.csv @@ -1,3 +1,3 @@ -1,12,4, -2,34,12, -3,56,21, +1.0,12.0,4.0, +2.0,34.0,12.0, +3.0,56.0,21.0, diff --git a/xchart-demo/CSV/CSVChartRowsExport/series1.csv b/xchart-demo/CSV/CSVChartRowsExport/series1.csv index 7a37b37bfa0abfd5f6ac1b8e3e743207628f1238..92e3bfa8c0f382c63543cadc71deb0bb5ccb6bf4 100644 --- a/xchart-demo/CSV/CSVChartRowsExport/series1.csv +++ b/xchart-demo/CSV/CSVChartRowsExport/series1.csv @@ -1,3 +1,3 @@ -1,2,3 -12,34,56 -4,12,21 +1.0,2.0,3.0 +12.0,34.0,56.0 +4.0,12.0,21.0 diff --git a/xchart-demo/pom.xml b/xchart-demo/pom.xml index 6b1858cc2676fb02f3b0a6c72a47efc4534b9e57..ad3f5caaf7dd21b82324d5c34f8e36abf4656816 100644 --- a/xchart-demo/pom.xml +++ b/xchart-demo/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>com.xeiam.xchart</groupId> <artifactId>xchart-parent</artifactId> - <version>2.3.3-SNAPSHOT</version> + <version>2.4.1-SNAPSHOT</version> </parent> <artifactId>xchart-demo</artifactId> @@ -17,7 +17,7 @@ <dependency> <groupId>com.xeiam.xchart</groupId> <artifactId>xchart</artifactId> - <version>2.3.3-SNAPSHOT</version> + <version>2.4.1-SNAPSHOT</version> </dependency> </dependencies> diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/bar/BarChart02.java b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/bar/BarChart02.java index db89c5f4a36476682e1ce3fcc5974e4c09b1125f..b9db53f76153a9632368e7de0663d63d2e54a8a7 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/bar/BarChart02.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/bar/BarChart02.java @@ -73,7 +73,7 @@ public class BarChart02 implements ExampleChart { e.printStackTrace(); } xData.add(date); - yData.add(-1 * ((random.nextInt(i) + 1))); + yData.add(-1 * 0.00000001 * ((random.nextInt(i) + 1))); } Series series = chart.addSeries("Model 77", xData, yData); series.setLineColor(SeriesColor.RED); diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/date/DateChart03.java b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/date/DateChart03.java index 5d9786aef032876e0e09591760767b3bfd6620ad..dbb32958ee0a52e28a6bfe7d90cb4c2f064638bf 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/date/DateChart03.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/date/DateChart03.java @@ -53,18 +53,18 @@ public class DateChart03 implements ExampleChart { Random random = new Random(); - DateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); + DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss.SSS"); Date date = null; for (int i = 1; i <= 14; i++) { try { - date = sdf.parse("08:" + (5 * i + random.nextInt(2)) + ":" + (random.nextInt(2)) + "." + random.nextInt(1000)); + date = sdf.parse("2013-07-22-08:" + (5 * i + random.nextInt(2)) + ":" + (random.nextInt(2)) + "." + random.nextInt(1000)); } catch (ParseException e) { e.printStackTrace(); } // System.out.println(date.getTime()); // System.out.println(date.toString()); xData.add(date); - yData.add(Math.random() * i); + yData.add(Math.random() * i * 1000000000); } chart.addSeries("blah", xData, yData); diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/date/DateChart04.java b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/date/DateChart04.java index cdc842dc5d0eda9a797e8448f648b9171c1da7e7..c3b2a52745c2349642cc92d3caad58ebbde748b5 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/date/DateChart04.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/date/DateChart04.java @@ -62,7 +62,7 @@ public class DateChart04 implements ExampleChart { e.printStackTrace(); } xData.add(date); - yData.add(Math.random() * i); + yData.add(Math.random() * i / 10000000000.0); } chart.addSeries("blah", xData, yData); diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/date/DateChart05.java b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/date/DateChart05.java index 7bca4d329d896e4b4aee3595feaeffefed460d20..39d9b8040a88b0ec0138bac0600cf5d121605d11 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/date/DateChart05.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/date/DateChart05.java @@ -62,7 +62,7 @@ public class DateChart05 implements ExampleChart { e.printStackTrace(); } xData.add(date); - yData.add(Math.random() * i); + yData.add(Math.random() * i / -100000000); } chart.addSeries("blah", xData, yData); diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart02.java b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart02.java index 3bbdb7a3695d822563275d35ba3e0b0be78fb7b5..070c412bb974ae3299008a9c0c932235ad60f587 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart02.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart02.java @@ -52,7 +52,7 @@ public class LineChart02 implements ExampleChart { for (int i = 0; i <= size; i++) { double radians = (Math.PI / (size / 2) * i); xData.add(i - size / 2); - yData.add(size * Math.sin(radians)); + yData.add(-.000001 * Math.sin(radians)); } // Create Chart 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 da94fc7a1c8fe32ba5c9c18b2d9fa6a6b331fc59..2a336a12cf9a173b33b5452f58fe6f3ff6a602e1 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 @@ -97,7 +97,7 @@ public class LineChart03 implements ExampleChart { chart.getStyleManager().setAxisTitleFont(new Font(Font.SANS_SERIF, Font.ITALIC, 18)); chart.getStyleManager().setAxisTickLabelsFont(new Font(Font.SERIF, Font.PLAIN, 11)); chart.getStyleManager().setDatePattern("dd-MMM"); - chart.getStyleManager().setNormalDecimalPattern("#0.000"); + chart.getStyleManager().setDecimalPattern("#0.000"); chart.getStyleManager().setLocale(Locale.GERMAN); Series series = chart.addSeries("Fake Data", xData, yData); diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart04.java b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart04.java index c36284fa50a3245ec3b2db73bbd77091cd9bc89b..cf367556fe834c735814113736d5b98e74cfe052 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart04.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart04.java @@ -48,7 +48,7 @@ public class LineChart04 implements ExampleChart { chart.getStyleManager().setLegendVisible(false); for (int i = 0; i < 200; i++) { - Series series = chart.addSeries("A" + i, new double[] { Math.random(), Math.random() }, new double[] { Math.random(), Math.random() }); + Series series = chart.addSeries("A" + i, new double[] { Math.random() / 1000, Math.random() / 1000 }, new double[] { Math.random() / -1000, Math.random() / -1000 }); series.setLineColor(SeriesColor.BLUE); series.setLineStyle(SeriesLineStyle.SOLID); series.setMarker(SeriesMarker.CIRCLE); diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/realtime/RealtimeChart01.java b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/realtime/RealtimeChart01.java index c96c243f8729254870008472f16ee2dfd3b8f12d..ce3828b9c9263291adbf23f9c62c9cc76f01898d 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/realtime/RealtimeChart01.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/realtime/RealtimeChart01.java @@ -100,7 +100,7 @@ public class RealtimeChart01 implements ExampleChart { return chart; } - private static List<Double> getRandomData(int numPoints) { + private List<Double> getRandomData(int numPoints) { List<Double> data = new ArrayList<Double>(); for (int i = 0; i < numPoints; i++) { diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/realtime/RealtimeChart03.java b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/realtime/RealtimeChart03.java new file mode 100644 index 0000000000000000000000000000000000000000..127c9d78245bc553c0fe6d684c860aa5d6c4b0af --- /dev/null +++ b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/realtime/RealtimeChart03.java @@ -0,0 +1,143 @@ +/** + * Copyright 2011 - 2014 Xeiam LLC. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.xeiam.xchart.demo.charts.realtime; + +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +import javax.swing.JFrame; + +import com.xeiam.xchart.Chart; +import com.xeiam.xchart.XChartPanel; +import com.xeiam.xchart.demo.charts.ExampleChart; + +/** + * Realtime + * <p> + * Demonstrates the following: + * <ul> + * <li>real-time chart updates + * <li>fixed window + * <li>error bars + */ +public class RealtimeChart03 implements ExampleChart { + + private List<Integer> xData = new ArrayList<Integer>(); + private List<Double> yData = new ArrayList<Double>(); + private List<Double> errorBars = new ArrayList<Double>(); + + public static final String SERIES_NAME = "series1"; + + public static void main(String[] args) { + + // Setup the panel + final RealtimeChart03 realtimeChart03 = new RealtimeChart03(); + final XChartPanel chartPanel = realtimeChart03.buildPanel(); + + // Schedule a job for the event-dispatching thread: + // creating and showing this application's GUI. + javax.swing.SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + + // Create and set up the window. + JFrame frame = new JFrame("XChart"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.add(chartPanel); + + // Display the window. + frame.pack(); + frame.setVisible(true); + } + }); + + // Simulate a data feed + TimerTask chartUpdaterTask = new TimerTask() { + + @Override + public void run() { + + realtimeChart03.updateData(); + chartPanel.updateSeries(SERIES_NAME, realtimeChart03.xData, realtimeChart03.getyData(), realtimeChart03.errorBars); + + } + }; + + Timer timer = new Timer(); + timer.scheduleAtFixedRate(chartUpdaterTask, 0, 500); + + } + + public XChartPanel buildPanel() { + + return new XChartPanel(getChart()); + } + + @Override + public Chart getChart() { + + yData.add(0.0); + for (int i = 0; i < 50; i++) { + double lastPoint = yData.get(yData.size() - 1); + yData.add(getRandomWalk(lastPoint)); + } + // generate X-Data + xData = new ArrayList<Integer>(); + for (int i = 1; i < yData.size() + 1; i++) { + xData.add(i); + } + // generate error bars + errorBars = new ArrayList<Double>(); + for (int i = 0; i < yData.size(); i++) { + errorBars.add(20 * Math.random()); + } + + // Create Chart + Chart chart = new Chart(500, 400); + chart.setChartTitle("Sample Real-time Chart"); + chart.setXAxisTitle("X"); + chart.setYAxisTitle("Y"); + chart.addSeries(SERIES_NAME, xData, yData, errorBars); + + return chart; + } + + private Double getRandomWalk(double lastPoint) { + + return lastPoint + (Math.random() * 100 - 50); + } + + public void updateData() { + + // Get some new data + double lastPoint = yData.get(yData.size() - 1); + yData.add(getRandomWalk(lastPoint)); + yData.remove(0); + + // update error bars + errorBars.add(20 * Math.random()); + errorBars.remove(0); + + } + + public List<Double> getyData() { + + return yData; + } +} diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/scatter/ScatterChart01.java b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/scatter/ScatterChart01.java index 7862a72a859653fda9286fe1ab22c488f33b1266..1fb2b3ea4ffcbaba8cede8ac143c674b1ac2fa2a 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/scatter/ScatterChart01.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/scatter/ScatterChart01.java @@ -32,6 +32,8 @@ import com.xeiam.xchart.demo.charts.ExampleChart; * <ul> * <li>ChartType.Scatter * <li>Series data as a Set + * <li>Setting marker size + * <li>Formatting of negative numbers with large magnitude but small differences */ public class ScatterChart01 implements ExampleChart { @@ -50,8 +52,8 @@ public class ScatterChart01 implements ExampleChart { Random random = new Random(); int size = 1000; for (int i = 0; i < size; i++) { - xData.add(random.nextGaussian()); - yData.add(random.nextGaussian()); + xData.add(random.nextGaussian() / 1000); + yData.add(-1000000 + random.nextGaussian()); } // Create Chart @@ -61,6 +63,7 @@ public class ScatterChart01 implements ExampleChart { // Customize Chart chart.getStyleManager().setChartTitleVisible(false); chart.getStyleManager().setLegendPosition(LegendPosition.InsideSW); + chart.getStyleManager().setMarkerSize(16); // Series chart.addSeries("Gaussian Blob", xData, yData); diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/scatter/ScatterChart02.java b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/scatter/ScatterChart02.java index cc7959570a9d54c91729536df3af42b33a889758..a3c0bd4c9b00b01489fae3dcdfcb1b2565a349e4 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/scatter/ScatterChart02.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/scatter/ScatterChart02.java @@ -33,6 +33,7 @@ import com.xeiam.xchart.demo.charts.ExampleChart; * <li>Scatter chart * <li>Logarithmic X-Axis * <li>Place legend at Inside-NW position + * <li>Formatting of number with large magnitude but small differences * * @author timmolter */ @@ -55,7 +56,7 @@ public class ScatterChart02 implements ExampleChart { for (int i = 0; i < size; i++) { double nextRandom = random.nextDouble(); xData.add(Math.pow(10, nextRandom * 10)); - yData.add(nextRandom + random.nextDouble()); + yData.add(1000000000.0 + nextRandom); } // Create Chart diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/scatter/ScatterChart04.java b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/scatter/ScatterChart04.java index 99a17fce522b92915333edaa114644dd3c675090..fb8eb1e67acd792f840933108c4bfaa548c07735 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/scatter/ScatterChart04.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/scatter/ScatterChart04.java @@ -51,11 +51,11 @@ public class ScatterChart04 implements ExampleChart { // generates data int size = 10; - List<Integer> xData = new ArrayList<Integer>(); + List<Double> xData = new ArrayList<Double>(); List<Double> yData = new ArrayList<Double>(); List<Double> errorBars = new ArrayList<Double>(); for (int i = 0; i <= size; i++) { - xData.add(i); + xData.add(((double) i) / 100000000); yData.add(10 * Math.exp(-i)); errorBars.add(Math.random() + .3); } diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/theme/ThemeChart01.java b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/theme/ThemeChart01.java index 9993b409c53c7db8665ab538fd64f5c48b3cdded..ec92f5f7c444b7dceff11741aaacf6c609a999a6 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/theme/ThemeChart01.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/theme/ThemeChart01.java @@ -24,6 +24,10 @@ import com.xeiam.xchart.demo.charts.ExampleChart; /** * Default XChart Theme + * <p> + * Demonstrates the following: + * <ul> + * <li>Setting marker size */ public class ThemeChart01 implements ExampleChart { @@ -58,6 +62,7 @@ public class ThemeChart01 implements ExampleChart { String seriesName = "y=" + 2 * i + "x-" + i * b + "b"; chart.addSeries(seriesName, xData, yData); + chart.getStyleManager().setMarkerSize(11); } return chart; diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/standalone/CSVChartColumns.java b/xchart-demo/src/main/java/com/xeiam/xchart/standalone/CSVChartColumns.java index 82b3a0e8a465ebcc2cc77d0607c54f4abb08b0ad..61a8cd531af4c29e4fd6c6b5054b54cd7c6ee384 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/standalone/CSVChartColumns.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/standalone/CSVChartColumns.java @@ -31,7 +31,7 @@ public class CSVChartColumns { // import chart from a folder containing CSV files Chart chart = CSVImporter.getChartFromCSVDir("./CSV/CSVChartColumns/", DataOrientation.Columns, 600, 600); - CSVExporter.writeCSVColumns(chart.getSeriesMap().get(0), "./CSV/CSVChartColumnsExport/"); + CSVExporter.writeCSVColumns(chart.getSeriesMap().get("series1"), "./CSV/CSVChartColumnsExport/"); // Show it new SwingWrapper(chart).displayChart(); diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/standalone/CSVChartRows.java b/xchart-demo/src/main/java/com/xeiam/xchart/standalone/CSVChartRows.java index 9d97331bdc2995505a794486927d5bdb24f172b0..be762d997e27264a7b7ed666b9fdab30876931cb 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/standalone/CSVChartRows.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/standalone/CSVChartRows.java @@ -31,7 +31,7 @@ public class CSVChartRows { // import chart from a folder containing CSV files Chart chart = CSVImporter.getChartFromCSVDir("./CSV/CSVChartRows/", DataOrientation.Rows, 600, 400); - CSVExporter.writeCSVRows(chart.getSeriesMap().get(0), "./CSV/CSVChartRowsExport/"); + CSVExporter.writeCSVRows(chart.getSeriesMap().get("series1"), "./CSV/CSVChartRowsExport/"); // Show it new SwingWrapper(chart).displayChart(); diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/standalone/DateChart.java b/xchart-demo/src/main/java/com/xeiam/xchart/standalone/DateChart.java new file mode 100644 index 0000000000000000000000000000000000000000..2c014b4e6fbf4f10cea9b0c193de066f8267f991 --- /dev/null +++ b/xchart-demo/src/main/java/com/xeiam/xchart/standalone/DateChart.java @@ -0,0 +1,75 @@ +/** + * Copyright 2013 Xeiam LLC. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.xeiam.xchart.standalone; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Random; + +import com.xeiam.xchart.Chart; +import com.xeiam.xchart.ChartBuilder; +import com.xeiam.xchart.SwingWrapper; +import com.xeiam.xchart.demo.charts.ExampleChart; + +/** + * @author timmolter + */ +public class DateChart implements ExampleChart { + + public static void main(String[] args) { + + ExampleChart exampleChart = new DateChart(); + Chart chart = exampleChart.getChart(); + new SwingWrapper(chart).displayChart(); + } + + @Override + public Chart getChart() { + + // Create Chart + Chart chart = new ChartBuilder().width(800).height(600).title("Day Scale").build(); + chart.getStyleManager().setLegendVisible(false); + + // generate data + List<Date> xData = new ArrayList<Date>(); + List<Double> yData = new ArrayList<Double>(); + + Random random = new Random(); + + DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + Date date = null; + for (int i = 1; i <= 5; i++) { + try { + date = sdf.parse("2014-02-" + i); + } catch (ParseException e) { + e.printStackTrace(); + } + System.out.println(date); + xData.add(date); + System.out.println(date.getTime()); + yData.add(Math.random() * i); + } + + chart.addSeries("blah", xData, yData); + + return chart; + + } +} diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/standalone/Example0.java b/xchart-demo/src/main/java/com/xeiam/xchart/standalone/Example0.java index 4e0a6fbecd27e419f40e0ea4eb920d75baa6c180..c50d3d0696cb65460526189f54c7837a9d58b4b9 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/standalone/Example0.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/standalone/Example0.java @@ -15,7 +15,6 @@ */ package com.xeiam.xchart.standalone; -import com.xeiam.xchart.BitmapEncoder; import com.xeiam.xchart.Chart; import com.xeiam.xchart.QuickChart; import com.xeiam.xchart.SwingWrapper; @@ -36,9 +35,5 @@ public class Example0 { // Show it new SwingWrapper(chart).displayChart(); - // Save it - BitmapEncoder.savePNG(chart, "./Sample_Chart.png"); - BitmapEncoder.savePNGWithDPI(chart, "./Sample_Chart_300_DPI.png", 300); - } } 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 f8a2080df5299cca98f2301d9d6b3e98ba96ec03..17c5095fb2f63a770d0a00e466808d3c6d100b30 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 @@ -16,9 +16,12 @@ package com.xeiam.xchart.standalone; import com.xeiam.xchart.BitmapEncoder; +import com.xeiam.xchart.BitmapEncoder.BitmapFormat; import com.xeiam.xchart.Chart; import com.xeiam.xchart.Series; import com.xeiam.xchart.SeriesMarker; +import com.xeiam.xchart.VectorGraphicsEncoder; +import com.xeiam.xchart.VectorGraphicsEncoder.VectorGraphicsFormat; /** * Creates a simple Chart and saves it as a PNG and JPEG image file. @@ -37,9 +40,19 @@ public class Example1 { Series series = chart.addSeries("y(x)", null, yData); series.setMarker(SeriesMarker.CIRCLE); - BitmapEncoder.savePNG(chart, "./Sample_Chart.png"); - BitmapEncoder.savePNGWithDPI(chart, "./Sample_Chart_300_DPI.png", 300); - BitmapEncoder.saveJPG(chart, "./Sample_Chart.jpg", 0.95f); + BitmapEncoder.saveBitmap(chart, "./Sample_Chart", BitmapFormat.PNG); + BitmapEncoder.saveBitmap(chart, "./Sample_Chart", BitmapFormat.JPG); + BitmapEncoder.saveJPGWithQuality(chart, "./Sample_Chart_With_Quality.jpg", 0.95f); + BitmapEncoder.saveBitmap(chart, "./Sample_Chart", BitmapFormat.BMP); + BitmapEncoder.saveBitmap(chart, "./Sample_Chart", BitmapFormat.GIF); + + BitmapEncoder.saveBitmapWithDPI(chart, "./Sample_Chart_300_DPI", BitmapFormat.PNG, 300); + BitmapEncoder.saveBitmapWithDPI(chart, "./Sample_Chart_300_DPI", BitmapFormat.JPG, 300); + BitmapEncoder.saveBitmapWithDPI(chart, "./Sample_Chart_300_DPI", BitmapFormat.GIF, 300); + + VectorGraphicsEncoder.saveVectorGraphic(chart, "./Sample_Chart", VectorGraphicsFormat.EPS); + VectorGraphicsEncoder.saveVectorGraphic(chart, "./Sample_Chart", VectorGraphicsFormat.PDF); + VectorGraphicsEncoder.saveVectorGraphic(chart, "./Sample_Chart", VectorGraphicsFormat.SVG); } } diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/standalone/VectorGraphicsTest.java b/xchart-demo/src/main/java/com/xeiam/xchart/standalone/VectorGraphicsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..33f2877e61e4e268c165d7314295e599ce28bd24 --- /dev/null +++ b/xchart-demo/src/main/java/com/xeiam/xchart/standalone/VectorGraphicsTest.java @@ -0,0 +1,41 @@ +/** + * Copyright 2013 Xeiam LLC. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.xeiam.xchart.standalone; + +import java.io.IOException; + +import com.xeiam.xchart.Chart; +import com.xeiam.xchart.QuickChart; +import com.xeiam.xchart.VectorGraphicsEncoder; +import com.xeiam.xchart.VectorGraphicsEncoder.VectorGraphicsFormat; + +/** + * @author timmolter + */ +public class VectorGraphicsTest { + + public static void main(String[] args) throws IOException { + + double[] xData = new double[] { 0.0, 1.0, 2.0 }; + double[] yData = new double[] { 2.0, 1.0, 0.0 }; + + // Create Chart + Chart chart = QuickChart.getChart("Sample Chart", "X", "Y", "y(x)", xData, yData); + + VectorGraphicsEncoder.saveVectorGraphic(chart, "", VectorGraphicsFormat.PDF); + } + +} diff --git a/xchart/pom.xml b/xchart/pom.xml index 23fb06a167859d37af875829a9b5a67d5b2fea20..49c9ae8f008b1482817e973240af124483ab7cbb 100644 --- a/xchart/pom.xml +++ b/xchart/pom.xml @@ -1,11 +1,11 @@ <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> - + <parent> <groupId>com.xeiam.xchart</groupId> <artifactId>xchart-parent</artifactId> - <version>2.3.3-SNAPSHOT</version> + <version>2.4.1-SNAPSHOT</version> </parent> <artifactId>xchart</artifactId> @@ -13,4 +13,11 @@ <name>XChart</name> <description>The core XChart library</description> + <dependencies> + <dependency> + <groupId>de.erichseifert.vectorgraphics2d</groupId> + <artifactId>VectorGraphics2D</artifactId> + </dependency> + </dependencies> + </project> diff --git a/xchart/src/main/java/com/xeiam/xchart/BitmapEncoder.java b/xchart/src/main/java/com/xeiam/xchart/BitmapEncoder.java index 84c0736f8b041ebb96097d7f986bff50acfa55cb..09661e273ef457191d3d0e6ff00a5aaa0d2206ad 100644 --- a/xchart/src/main/java/com/xeiam/xchart/BitmapEncoder.java +++ b/xchart/src/main/java/com/xeiam/xchart/BitmapEncoder.java @@ -50,20 +50,24 @@ public final class BitmapEncoder { } + public enum BitmapFormat { + PNG, JPG, BMP, GIF; + } + /** - * Save a Chart as a PNG file + * Save a Chart as an image file * * @param chart * @param fileName + * @param bitmapFormat * @throws IOException */ - public static void savePNG(Chart chart, String fileName) throws IOException { + public static void saveBitmap(Chart chart, String fileName, BitmapFormat bitmapFormat) throws IOException { BufferedImage bufferedImage = getBufferedImage(chart); - // Save chart as PNG - OutputStream out = new FileOutputStream(fileName); - ImageIO.write(bufferedImage, "png", out); + OutputStream out = new FileOutputStream(fileName + "." + bitmapFormat.toString().toLowerCase()); + ImageIO.write(bufferedImage, bitmapFormat.toString().toLowerCase(), out); out.close(); } @@ -75,7 +79,7 @@ public final class BitmapEncoder { * @param DPI * @throws IOException */ - public static void savePNGWithDPI(Chart chart, String fileName, int DPI) throws IOException { + public static void saveBitmapWithDPI(Chart chart, String fileName, BitmapFormat bitmapFormat, int DPI) throws IOException { double scaleFactor = DPI / 72.0; @@ -88,27 +92,26 @@ public final class BitmapEncoder { graphics2D.setTransform(at); chart.paint(graphics2D, chart.getWidth(), chart.getHeight()); - - for (Iterator<ImageWriter> iw = ImageIO.getImageWritersByFormatName("png"); iw.hasNext();) { - ImageWriter writer = iw.next(); + Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(bitmapFormat.toString().toLowerCase()); + if (writers.hasNext()) { + ImageWriter writer = writers.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; + throw new IllegalArgumentException("It is not possible to set the DPI on a bitmap with " + bitmapFormat + " format!! Try another format."); } - setDPIforPNG(metadata, DPI); + setDPI(metadata, DPI); - File file = new File(fileName); + File file = new File(fileName + "." + bitmapFormat.toString().toLowerCase()); FileImageOutputStream output = new FileImageOutputStream(file); writer.setOutput(output); IIOImage image = new IIOImage(bufferedImage, null, metadata); writer.write(null, image, iwp); writer.dispose(); - break; } } @@ -119,7 +122,7 @@ public final class BitmapEncoder { * @param DPI * @throws IIOInvalidTreeException */ - private static void setDPIforPNG(IIOMetadata metadata, int DPI) throws IIOInvalidTreeException { + private static void setDPI(IIOMetadata metadata, int DPI) throws IIOInvalidTreeException { // for PNG, it's dots per millimeter double dotsPerMilli = 1.0 * DPI / 10 / 2.54; @@ -149,7 +152,7 @@ public final class BitmapEncoder { * @throws FileNotFoundException * @throws IOException */ - public static void saveJPG(Chart chart, String fileName, float quality) throws FileNotFoundException, IOException { + public static void saveJPGWithQuality(Chart chart, String fileName, float quality) throws FileNotFoundException, IOException { BufferedImage bufferedImage = getBufferedImage(chart); @@ -174,14 +177,14 @@ public final class BitmapEncoder { * @return a byte[] for a given chart, PNG compressed * @throws IOException */ - public static byte[] getPNGBytes(Chart chart) throws IOException { + public static byte[] getBitmapBytes(Chart chart, BitmapFormat bitmapFormat) throws IOException { BufferedImage bufferedImage = getBufferedImage(chart); byte[] imageInBytes = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ImageIO.write(bufferedImage, "png", baos); + ImageIO.write(bufferedImage, bitmapFormat.toString().toLowerCase(), baos); baos.flush(); imageInBytes = baos.toByteArray(); baos.close(); diff --git a/xchart/src/main/java/com/xeiam/xchart/CSVImporter.java b/xchart/src/main/java/com/xeiam/xchart/CSVImporter.java index 166227c62f81d49cc7f3f6c0b53180bcf06f1776..e48df88f317d972af3916a18b763ed714d7a51b4 100644 --- a/xchart/src/main/java/com/xeiam/xchart/CSVImporter.java +++ b/xchart/src/main/java/com/xeiam/xchart/CSVImporter.java @@ -19,7 +19,6 @@ import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; -import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -177,7 +176,7 @@ public class CSVImporter { for (int i = 0; i < stringDataArray.length; i++) { String dataPoint = stringDataArray[i]; try { - BigDecimal value = new BigDecimal(dataPoint); + Double value = Double.parseDouble(dataPoint); axisData.add(value); } catch (NumberFormatException e) { System.out.println("Error parsing >" + dataPoint + "< !"); diff --git a/xchart/src/main/java/com/xeiam/xchart/Histogram.java b/xchart/src/main/java/com/xeiam/xchart/Histogram.java index 38b2a90e31a11975b5ecf627f27b5ba8c8cf979c..31cb3b9879c66d6d3e887198c1d40b8ec262a186 100644 --- a/xchart/src/main/java/com/xeiam/xchart/Histogram.java +++ b/xchart/src/main/java/com/xeiam/xchart/Histogram.java @@ -70,11 +70,8 @@ public class Histogram { this.numBins = numBins; this.originalData = data; - // Arrays.sort(data); this.min = min; this.max = max; - // this.min = data[0]; - // this.max = data[data.length - 1]; init(); } @@ -90,8 +87,13 @@ public class Histogram { int bin = (int) ((((Number) itr.next()).doubleValue() - min) / binSize); // changed this from numBins if (bin < 0) { /* this data is smaller than min */ + // System.out.println("less than"); } - else if (bin >= numBins) { /* this data point is bigger than max */ + else if (bin > numBins) { /* this data point is bigger than max */ + // System.out.println("greater than"); + } + else if (bin == numBins) { // this falls right on the edge of the max bin + tempYAxisData[bin - 1] += 1; } else { tempYAxisData[bin] += 1; diff --git a/xchart/src/main/java/com/xeiam/xchart/Series.java b/xchart/src/main/java/com/xeiam/xchart/Series.java index 213dd15776ef8387b3d361005d84eadcf29be638..254bcbad413c7cc2dc28439a4de1896ebc1d8fea 100644 --- a/xchart/src/main/java/com/xeiam/xchart/Series.java +++ b/xchart/src/main/java/com/xeiam/xchart/Series.java @@ -107,7 +107,7 @@ public class Series { private double[] findMinMax(Collection<?> data, AxisType axisType) { double min = Double.MAX_VALUE; - double max = Double.MIN_VALUE; + double max = -Double.MAX_VALUE; for (Object dataPoint : data) { @@ -119,7 +119,6 @@ public class Series { if (axisType == AxisType.Number) { value = ((Number) dataPoint).doubleValue(); - } else if (axisType == AxisType.Date) { Date date = (Date) dataPoint; @@ -148,7 +147,7 @@ public class Series { private double[] findMinMaxWithErrorBars(Collection<? extends Number> data, Collection<? extends Number> errorBars) { double min = Double.MAX_VALUE; - double max = Double.MIN_VALUE; + double max = -Double.MAX_VALUE; Iterator<? extends Number> itr = data.iterator(); Iterator<? extends Number> ebItr = errorBars.iterator(); @@ -302,18 +301,24 @@ public class Series { return name; } - void replaceXData(Collection<?> newXData) { + public void replaceXData(Collection<?> newXData) { xData = newXData; calculateMinMax(); } - void replaceYData(Collection<? extends Number> newYData) { + public void replaceYData(Collection<? extends Number> newYData) { yData = newYData; calculateMinMax(); } + public void replaceErrroBarData(Collection<? extends Number> newErrorBars) { + + errorBars = newErrorBars; + calculateMinMax(); + } + private void calculateMinMax() { // xData diff --git a/xchart/src/main/java/com/xeiam/xchart/StyleManager.java b/xchart/src/main/java/com/xeiam/xchart/StyleManager.java index 5b50fa7965ded5c1a0b952a828130327d76e5969..0856bb7006d4bdb8392bea39dd17d851a26f2f5c 100644 --- a/xchart/src/main/java/com/xeiam/xchart/StyleManager.java +++ b/xchart/src/main/java/com/xeiam/xchart/StyleManager.java @@ -129,17 +129,19 @@ public class StyleManager { // Bar Charts /////////////////////////////// private double barWidthPercentage; - private boolean barsOverlapped; + private boolean isBarsOverlapped; + + // Line, Scatter, Area Charts /////////////////////////////// + private int markerSize; // Error Bars /////////////////////////////// private Color errorBarsColor; // Formatting //////////////////////////////// - private String datePattern; private Locale locale; private TimeZone timezone; - private String normalDecimalPattern; - private String scientificDecimalPattern; + private String datePattern; + private String decimalPattern; /** * Constructor @@ -211,17 +213,20 @@ public class StyleManager { // Bar Charts /////////////////////////////// barWidthPercentage = theme.getBarWidthPercentage(); - barsOverlapped = theme.barsOverlapped(); + isBarsOverlapped = theme.isBarsOverlapped(); + + // Line, Scatter, Area Charts /////////////////////////////// + + markerSize = theme.getMarkerSize(); // Error Bars /////////////////////////////// errorBarsColor = theme.getErrorBarsColor(); // Formatting //////////////////////////////// - datePattern = null; // if not null, this override pattern will be used locale = Locale.getDefault(); timezone = TimeZone.getDefault(); - normalDecimalPattern = "#.####"; - scientificDecimalPattern = "0.##E0"; + datePattern = null; // if not null, this override pattern will be used + decimalPattern = null; } /** @@ -994,52 +999,54 @@ public class StyleManager { /** * set whether or no bars are overlapped. Otherwise they are places side-by-side * - * @param barsOverlapped + * @param isBarsOverlapped */ - public void setBarsOverlapped(boolean barsOverlapped) { + public void setBarsOverlapped(boolean isBarsOverlapped) { - this.barsOverlapped = barsOverlapped; + this.isBarsOverlapped = isBarsOverlapped; } - public boolean barsOverlapped() { + public boolean isBarsOverlapped() { - return barsOverlapped; + return isBarsOverlapped; } - // Error Bars /////////////////////////////// + // Line, Scatter, Area Charts /////////////////////////////// /** - * Sets the color of the error bars + * Sets the size of the markers in pixels * - * @param errorBarsColor + * @param markerSize */ - public void setErrorBarsColor(Color errorBarsColor) { + public void setMarkerSize(int markerSize) { - this.errorBarsColor = errorBarsColor; + this.markerSize = markerSize; } - public Color getErrorBarsColor() { + public int getMarkerSize() { - return errorBarsColor; + return markerSize; } - // Formatting //////////////////////////////// + // Error Bars /////////////////////////////// /** - * Set the String formatter for Data x-axis + * Sets the color of the error bars * - * @param pattern - the pattern describing the date and time format + * @param errorBarsColor */ - public void setDatePattern(String datePattern) { + public void setErrorBarsColor(Color errorBarsColor) { - this.datePattern = datePattern; + this.errorBarsColor = errorBarsColor; } - public String getDatePattern() { + public Color getErrorBarsColor() { - return datePattern; + return errorBarsColor; } + // Formatting //////////////////////////////// + /** * Set the locale to use for rendering the chart * @@ -1071,32 +1078,33 @@ public class StyleManager { } /** - * Set the decimal formatter for all tick labels + * Set the String formatter for Data x-axis * - * @param pattern - the pattern describing the decimal format + * @param pattern - the pattern describing the date and time format */ - public void setNormalDecimalPattern(String normalDecimalPattern) { + public void setDatePattern(String datePattern) { - this.normalDecimalPattern = normalDecimalPattern; + this.datePattern = datePattern; } - public String getNormalDecimalPattern() { + public String getDatePattern() { - return normalDecimalPattern; + return datePattern; } /** - * Set the scientific notation formatter for all tick labels + * Set the decimal formatter for all tick labels * - * @param pattern - the pattern describing the scientific notation format + * @param pattern - the pattern describing the decimal format */ - public void setScientificDecimalPattern(String scientificDecimalPattern) { + public void setDecimalPattern(String decimalPattern) { - this.scientificDecimalPattern = scientificDecimalPattern; + this.decimalPattern = decimalPattern; } - public String getScientificDecimalPattern() { + public String getDecimalPattern() { - return scientificDecimalPattern; + return decimalPattern; } + } diff --git a/xchart/src/main/java/com/xeiam/xchart/VectorGraphicsEncoder.java b/xchart/src/main/java/com/xeiam/xchart/VectorGraphicsEncoder.java new file mode 100644 index 0000000000000000000000000000000000000000..fc00d7de4f4a7160648cfb8eeafdcb8f931a02b7 --- /dev/null +++ b/xchart/src/main/java/com/xeiam/xchart/VectorGraphicsEncoder.java @@ -0,0 +1,75 @@ +/** + * Copyright 2011 - 2014 Xeiam LLC. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.xeiam.xchart; + +import java.io.FileOutputStream; +import java.io.IOException; + +import de.erichseifert.vectorgraphics2d.EPSGraphics2D; +import de.erichseifert.vectorgraphics2d.PDFGraphics2D; +import de.erichseifert.vectorgraphics2d.SVGGraphics2D; +import de.erichseifert.vectorgraphics2d.VectorGraphics2D; + +/** + * A helper class with static methods for saving Charts as bitmaps + * + * @author timmolter + */ +public final class VectorGraphicsEncoder { + + /** + * Constructor - Private constructor to prevent instantiation + */ + private VectorGraphicsEncoder() { + + } + + public enum VectorGraphicsFormat { + EPS, PDF, SVG; + } + + public static void saveVectorGraphic(Chart chart, String fileName, VectorGraphicsFormat vectorGraphicsFormat) throws IOException { + + VectorGraphics2D g = null; + + switch (vectorGraphicsFormat) { + case EPS: + g = new EPSGraphics2D(0.0, 0.0, chart.getWidth(), chart.getHeight()); + break; + case PDF: + g = new PDFGraphics2D(0.0, 0.0, chart.getWidth(), chart.getHeight()); + break; + case SVG: + g = new SVGGraphics2D(0.0, 0.0, chart.getWidth(), chart.getHeight()); + break; + + default: + break; + } + + chart.paint(g, chart.getWidth(), chart.getHeight()); + + // Write the vector graphic output to a file + FileOutputStream file = new FileOutputStream(fileName + "." + vectorGraphicsFormat.toString().toLowerCase()); + + try { + file.write(g.getBytes()); + } finally { + file.close(); + } + } + +} diff --git a/xchart/src/main/java/com/xeiam/xchart/XChartPanel.java b/xchart/src/main/java/com/xeiam/xchart/XChartPanel.java index 545e28764bcc1f09d705eb10f118443e71ce2a1c..f9795f15c07b2306f1aa07b4d7fe0fbbdf2b53ab 100644 --- a/xchart/src/main/java/com/xeiam/xchart/XChartPanel.java +++ b/xchart/src/main/java/com/xeiam/xchart/XChartPanel.java @@ -38,6 +38,8 @@ import javax.swing.JPopupMenu; import javax.swing.KeyStroke; import javax.swing.filechooser.FileFilter; +import com.xeiam.xchart.BitmapEncoder.BitmapFormat; + /** * A Swing JPanel that contains a Chart * <p> @@ -111,22 +113,33 @@ public class XChartPanel extends JPanel { JFileChooser fileChooser = new JFileChooser(); fileChooser.addChoosableFileFilter(new JPGSaveFilter()); - fileChooser.addChoosableFileFilter(new PNGSaveFilter()); + FileFilter pngFileFilter = new PNGSaveFilter(); + fileChooser.addChoosableFileFilter(pngFileFilter); + fileChooser.addChoosableFileFilter(new BMPSaveFilter()); + fileChooser.addChoosableFileFilter(new GIFSaveFilter()); fileChooser.setAcceptAllFileFilterUsed(false); + fileChooser.setFileFilter(pngFileFilter); + if (fileChooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { if (fileChooser.getSelectedFile() != null) { File theFileToSave = fileChooser.getSelectedFile(); try { if (fileChooser.getFileFilter() == null) { - BitmapEncoder.savePNG(chart, theFileToSave.getCanonicalPath().toString()); + BitmapEncoder.saveBitmap(chart, theFileToSave.getCanonicalPath().toString(), BitmapFormat.PNG); } else if (fileChooser.getFileFilter().getDescription().equals("*.jpg,*.JPG")) { - BitmapEncoder.saveJPG(chart, theFileToSave.getCanonicalPath().toString() + ".jpg", 1.0f); + BitmapEncoder.saveJPGWithQuality(chart, theFileToSave.getCanonicalPath().toString() + ".jpg", 1.0f); } else if (fileChooser.getFileFilter().getDescription().equals("*.png,*.PNG")) { - BitmapEncoder.savePNG(chart, theFileToSave.getCanonicalPath().toString() + ".png"); + BitmapEncoder.saveBitmap(chart, theFileToSave.getCanonicalPath().toString(), BitmapFormat.PNG); + } + else if (fileChooser.getFileFilter().getDescription().equals("*.bmp,*.BMP")) { + BitmapEncoder.saveBitmap(chart, theFileToSave.getCanonicalPath().toString(), BitmapFormat.BMP); + } + else if (fileChooser.getFileFilter().getDescription().equals("*.gif,*.GIF")) { + BitmapEncoder.saveBitmap(chart, theFileToSave.getCanonicalPath().toString(), BitmapFormat.GIF); } } catch (IOException e) { e.printStackTrace(); @@ -158,6 +171,50 @@ public class XChartPanel extends JPanel { } + private class BMPSaveFilter extends FileFilter { + + @Override + public boolean accept(File f) { + + if (f.isDirectory()) { + return false; + } + + String s = f.getName(); + + return s.endsWith(".bmp") || s.endsWith(".BMP"); + } + + @Override + public String getDescription() { + + return "*.bmp,*.BMP"; + } + + } + + private class GIFSaveFilter extends FileFilter { + + @Override + public boolean accept(File f) { + + if (f.isDirectory()) { + return false; + } + + String s = f.getName(); + + return s.endsWith(".gif") || s.endsWith(".GIF"); + } + + @Override + public String getDescription() { + + return "*.gif,*.GIF"; + } + + } + private class PNGSaveFilter extends FileFilter { @Override @@ -261,7 +318,7 @@ public class XChartPanel extends JPanel { series.replaceYData(newYData); // generate X-Data - List<Number> generatedXData = new ArrayList<Number>(); + List<Integer> generatedXData = new ArrayList<Integer>(); for (int i = 1; i < newYData.size() + 1; i++) { generatedXData.add(i); } @@ -296,4 +353,30 @@ public class XChartPanel extends JPanel { return series; } + + /** + * update a series by updating the X-Axis, Y-Axis and error bar data + * + * @param seriesName + * @param newXData + * @param newYData + * @param newErrorBarData + * @return + */ + public Series updateSeries(String seriesName, Collection<?> newXData, List<? extends Number> newYData, List<? extends Number> newErrorBarData) { + + Series series = chart.getSeriesMap().get(seriesName); + if (series == null) { + throw new IllegalArgumentException("Series name >" + seriesName + "< not found!!!"); + } + series.replaceXData(newXData); + series.replaceYData(newYData); + series.replaceErrroBarData(newErrorBarData); + + // Re-display the chart + revalidate(); + repaint(); + + return series; + } } 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 56360dc9f072d38d0f92936629ff0d1f86816c48..fa80957328352078e9578269eea1d61aedd14ef8 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 @@ -49,9 +49,9 @@ public class Axis implements ChartPart { /** the axis direction */ private Direction direction; - private double min;; + private double min; - private double max;; + private double max; /** the bounds */ private Rectangle2D bounds; @@ -84,12 +84,12 @@ public class Axis implements ChartPart { } /** - * Reset the default min and max values in preparation for calculating the actualy min and max + * Reset the default min and max values in preparation for calculating the actual min and max */ void resetMinMax() { min = Double.MAX_VALUE; - max = Double.MIN_VALUE; + max = -Double.MAX_VALUE; } /** diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisPair.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisPair.java index e3bc3a42fcc28de4325700d7c41b56ada923b32d..03eb1fae219f7bac83860f807d9d66951d32896c 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisPair.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisPair.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map; import com.xeiam.xchart.Series; +import com.xeiam.xchart.StyleManager.ChartType; import com.xeiam.xchart.internal.chartpart.Axis.AxisType; import com.xeiam.xchart.internal.style.SeriesColorMarkerLineStyleCycler; @@ -46,7 +47,7 @@ public class AxisPair implements ChartPart { /** * Constructor - * + * * @param the parent chartPainter */ public AxisPair(ChartPainter chartPainter) { @@ -93,6 +94,9 @@ public class AxisPair implements ChartPart { xAxis.setAxisType(AxisType.Date); } else if (dataPoint instanceof String) { + if (getChartPainter().getStyleManager().getChartType() != ChartType.Bar) { + throw new RuntimeException("X-Axis data types of String can only be used for Bar Charts!!!"); + } xAxis.setAxisType(AxisType.String); } else { diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTick.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTick.java index 86689299279bd6b3cf8cd0d924a43799d7d177f6..1d2a4a9d35c333ef1b2fa8a1991aabe0a18a8fd8 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTick.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTick.java @@ -39,7 +39,7 @@ public class AxisTick implements ChartPart { /** the bounds */ private Rectangle2D bounds = new Rectangle2D.Double(); - AxisTickCalculator gridStep = null; + AxisTickCalculator axisTickCalculator = null; /** * Constructor @@ -62,39 +62,39 @@ public class AxisTick implements ChartPart { @Override public void paint(Graphics2D g) { - int workingSpace = 0; + double workingSpace = 0.0; if (axis.getDirection() == Axis.Direction.Y) { - workingSpace = (int) axis.getPaintZone().getHeight(); // number of pixels the axis has to work with for drawing AxisTicks + workingSpace = axis.getPaintZone().getHeight(); // number of pixels the axis has to work with for drawing AxisTicks // System.out.println("workingspace= " + workingSpace); } else if (axis.getDirection() == Axis.Direction.X) { - workingSpace = (int) axis.getPaintZone().getWidth(); // number of pixels the axis has to work with for drawing AxisTicks + workingSpace = axis.getPaintZone().getWidth(); // number of pixels the axis has to work with for drawing AxisTicks // System.out.println("workingspace= " + workingSpace); } if (axis.getDirection() == Axis.Direction.X && getChartPainter().getStyleManager().getChartType() == ChartType.Bar) { - gridStep = new AxisTickBarChartCalculator(axis.getDirection(), workingSpace, axis.getMin(), axis.getMax(), getChartPainter()); + axisTickCalculator = new AxisTickBarChartCalculator(axis.getDirection(), workingSpace, axis.getMin(), axis.getMax(), getChartPainter()); } else if (axis.getDirection() == Axis.Direction.X && getChartPainter().getStyleManager().isXAxisLogarithmic() && axis.getAxisType() != AxisType.Date) { - gridStep = new AxisTickLogarithmicCalculator(axis.getDirection(), workingSpace, axis.getMin(), axis.getMax(), getChartPainter().getStyleManager()); + axisTickCalculator = new AxisTickLogarithmicCalculator(axis.getDirection(), workingSpace, axis.getMin(), axis.getMax(), getChartPainter().getStyleManager()); } else if (axis.getDirection() == Axis.Direction.Y && getChartPainter().getStyleManager().isYAxisLogarithmic() && axis.getAxisType() != AxisType.Date) { - gridStep = new AxisTickLogarithmicCalculator(axis.getDirection(), workingSpace, axis.getMin(), axis.getMax(), getChartPainter().getStyleManager()); + axisTickCalculator = new AxisTickLogarithmicCalculator(axis.getDirection(), workingSpace, axis.getMin(), axis.getMax(), getChartPainter().getStyleManager()); } else if (axis.getAxisType() == AxisType.Number) { - gridStep = new AxisTickNumericalCalculator(axis.getDirection(), workingSpace, axis.getMin(), axis.getMax(), getChartPainter().getStyleManager()); + axisTickCalculator = new AxisTickNumericalCalculator(axis.getDirection(), workingSpace, axis.getMin(), axis.getMax(), getChartPainter().getStyleManager()); } else if (axis.getAxisType() == AxisType.Date) { - gridStep = new AxisTickDateCalculator(axis.getDirection(), workingSpace, axis.getMin(), axis.getMax(), getChartPainter().getStyleManager()); + axisTickCalculator = new AxisTickDateCalculator(axis.getDirection(), workingSpace, axis.getMin(), axis.getMax(), getChartPainter().getStyleManager()); } @@ -154,11 +154,11 @@ public class AxisTick implements ChartPart { public List<Double> getTickLocations() { - return gridStep.getTickLocations(); + return axisTickCalculator.getTickLocations(); } public List<String> getTickLabels() { - return gridStep.getTickLabels(); + return axisTickCalculator.getTickLabels(); } } diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickBarChartCalculator.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickBarChartCalculator.java index 565e2e27f2c905c3cc5717f380961149c5387a75..422bc51bd6f25ea88c16d49a1c3457f7b470ace8 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickBarChartCalculator.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickBarChartCalculator.java @@ -15,6 +15,7 @@ */ package com.xeiam.xchart.internal.chartpart; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; @@ -27,21 +28,21 @@ import com.xeiam.xchart.internal.chartpart.Axis.Direction; /** * This class encapsulates the logic to generate the axis tick mark and axis tick label data for rendering the axis ticks for decimal axes - * + * * @author timmolter */ public class AxisTickBarChartCalculator extends AxisTickCalculator { /** * Constructor - * + * * @param axisDirection * @param workingSpace * @param minValue * @param maxValue * @param styleManager */ - public AxisTickBarChartCalculator(Direction axisDirection, int workingSpace, double minValue, double maxValue, ChartPainter chart) { + public AxisTickBarChartCalculator(Direction axisDirection, double workingSpace, double minValue, double maxValue, ChartPainter chart) { super(axisDirection, workingSpace, minValue, maxValue, chart.getStyleManager()); calculate(chart); @@ -118,7 +119,7 @@ public class AxisTickBarChartCalculator extends AxisTickCalculator { for (Object category : categories) { if (chartPainter.getAxisPair().getXAxis().getAxisType() == AxisType.Number) { - tickLabels.add(numberFormatter.formatNumber((Double) category)); + tickLabels.add(numberFormatter.formatNumber(BigDecimal.valueOf((Double) category), minValue, maxValue)); } else if (chartPainter.getAxisPair().getXAxis().getAxisType() == AxisType.Date) { long span = (long) Math.abs(maxValue - minValue); // in data space @@ -146,10 +147,10 @@ public class AxisTickBarChartCalculator extends AxisTickCalculator { dateFormatter = new DateFormatter(chartPainter.getStyleManager()); } - for (double tickPosition = firstPosition; tickPosition <= maxValue; tickPosition = tickPosition + gridStep) { + for (double tickPosition = firstPosition; tickPosition <= maxValue + 2 * gridStep; tickPosition = tickPosition + gridStep) { if (chartPainter.getAxisPair().getXAxis().getAxisType() == AxisType.Number) { - tickLabels.add(numberFormatter.formatNumber(tickPosition)); + tickLabels.add(numberFormatter.formatNumber(BigDecimal.valueOf(tickPosition), minValue, maxValue)); } else if (chartPainter.getAxisPair().getXAxis().getAxisType() == AxisType.Date) { long span = (long) Math.abs(maxValue - minValue); // in data space diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickCalculator.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickCalculator.java index e3077239628bb88aeea149babc9680a9e2ee287e..a36884dd4a4458f229383bacbef602fa5fd60f78 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickCalculator.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickCalculator.java @@ -36,7 +36,7 @@ public abstract class AxisTickCalculator { protected final Direction axisDirection; - protected final int workingSpace; + protected final double workingSpace; protected final double minValue; @@ -46,14 +46,14 @@ public abstract class AxisTickCalculator { /** * Constructor - * + * * @param axisDirection * @param workingSpace * @param minValue * @param maxValue * @param styleManager */ - public AxisTickCalculator(Direction axisDirection, int workingSpace, double minValue, double maxValue, StyleManager styleManager) { + public AxisTickCalculator(Direction axisDirection, double workingSpace, double minValue, double maxValue, StyleManager styleManager) { // override min/max value for bar charts' Y-Axis double overrideMinValue = minValue; @@ -94,19 +94,20 @@ public abstract class AxisTickCalculator { /** * Gets the first position - * + * * @param gridStep * @return */ double getFirstPosition(double gridStep) { - double firstPosition; - if (minValue % gridStep <= 0.0) { - firstPosition = minValue - (minValue % gridStep); - } - else { - firstPosition = minValue - (minValue % gridStep) + gridStep; - } + // System.out.println("******"); + + // double firstPosition = minValue - (minValue % gridStep) + gridStep; + double firstPosition = minValue - (minValue % gridStep) - gridStep; + // + // if ((firstPosition - minValue) > gridStep) { + // firstPosition = minValue - (minValue % gridStep); + // } return firstPosition; } @@ -122,7 +123,7 @@ public abstract class AxisTickCalculator { /** * Determine the grid step for the data set given the space in pixels allocated for the axis - * + * * @param tickSpace in plot space * @return */ diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickDateCalculator.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickDateCalculator.java index a981741591c001f498d548f9cc24680564fa54b8..89963ba2d0546affc216fe29b2756b59a468fa60 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickDateCalculator.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickDateCalculator.java @@ -21,7 +21,7 @@ import com.xeiam.xchart.internal.chartpart.Axis.Direction; /** * This class encapsulates the logic to generate the axis tick mark and axis tick label data for rendering the axis ticks for date axes - * + * * @author timmolter */ public class AxisTickDateCalculator extends AxisTickCalculator { @@ -30,14 +30,14 @@ public class AxisTickDateCalculator extends AxisTickCalculator { /** * Constructor - * + * * @param axisDirection * @param workingSpace * @param minValue * @param maxValue * @param styleManager */ - public AxisTickDateCalculator(Direction axisDirection, int workingSpace, double minValue, double maxValue, StyleManager styleManager) { + public AxisTickDateCalculator(Direction axisDirection, double workingSpace, double minValue, double maxValue, StyleManager styleManager) { super(axisDirection, workingSpace, minValue, maxValue, styleManager); dateFormatter = new DateFormatter(styleManager); @@ -47,7 +47,7 @@ public class AxisTickDateCalculator extends AxisTickCalculator { private void calculate() { // tick space - a percentage of the working space available for ticks - int tickSpace = (int) (styleManager.getAxisTickSpaceRatio() * workingSpace); // in plot space + double tickSpace = styleManager.getAxisTickSpaceRatio() * workingSpace; // in plot space // where the tick should begin in the working space in pixels double margin = Utils.getTickStartOffset(workingSpace, tickSpace); // in plot space double gridStep = getGridStepForDecimal(tickSpace); @@ -55,7 +55,7 @@ public class AxisTickDateCalculator extends AxisTickCalculator { // the span of the data long span = (long) Math.abs(maxValue - minValue); // in data space - long gridStepHint = (long) (span / (double) tickSpace * styleManager.getXAxisTickMarkSpacingHint()); + long gridStepHint = (long) (span / tickSpace * styleManager.getXAxisTickMarkSpacingHint()); long timeUnit = dateFormatter.getTimeUnit(gridStepHint); double gridStep = 0.0; @@ -70,11 +70,11 @@ public class AxisTickDateCalculator extends AxisTickCalculator { double firstPosition = getFirstPosition(gridStep); // generate all tickLabels and tickLocations from the first to last position - for (double tickPosition = firstPosition; tickPosition <= maxValue; tickPosition = tickPosition + gridStep) { + for (double tickPosition = firstPosition; tickPosition <= maxValue + 2 * gridStep; tickPosition = tickPosition + gridStep) { tickLabels.add(dateFormatter.formatDate(tickPosition, timeUnit)); // here we convert tickPosition finally to plot space, i.e. pixels - double tickLabelPosition = (int) (margin + ((tickPosition - minValue) / (maxValue - minValue) * tickSpace)); + double tickLabelPosition = margin + ((tickPosition - minValue) / (maxValue - minValue) * tickSpace); tickLocations.add(tickLabelPosition); } } 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 5b851847dfb8c08cf7b00acd8859f67e49441d9b..a9d308043bc1601bae75352a57a3bf246bbfc8cf 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 @@ -16,8 +16,10 @@ package com.xeiam.xchart.internal.chartpart; import java.awt.Graphics2D; +import java.awt.Shape; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; /** @@ -33,7 +35,7 @@ public class AxisTickLabels implements ChartPart { /** * Constructor - * + * * @param axisTick */ protected AxisTickLabels(AxisTick axisTick) { @@ -58,36 +60,40 @@ public class AxisTickLabels implements ChartPart { double xOffset = axisTick.getAxis().getAxisTitle().getBounds().getX() + axisTick.getAxis().getAxisTitle().getBounds().getWidth(); double yOffset = axisTick.getAxis().getPaintZone().getY(); + double height = axisTick.getAxis().getPaintZone().getHeight(); double maxTickLabelWidth = 0; + for (int i = 0; i < axisTick.getTickLabels().size(); i++) { String tickLabel = axisTick.getTickLabels().get(i); // System.out.println(tickLabel); double tickLocation = axisTick.getTickLocations().get(i); + double flippedTickLocation = yOffset + height - tickLocation; - if (tickLabel != null) { // some are null for logarithmic axes - - // AffineTransform orig = g.getTransform(); - // AffineTransform at = new AffineTransform(); - // at.rotate(Math.PI / -2.0, xOffset, (float) (yOffset + axisTick.getAxis().getPaintZone().getHeight() - tickLocation / 2.0)); - // g.transform(at); + if (tickLabel != null && flippedTickLocation > yOffset && flippedTickLocation < yOffset + height) { // some are null for logarithmic axes FontRenderContext frc = g.getFontRenderContext(); - // TextLayout layout = new TextLayout(tickLabel, font, new FontRenderContext(null, true, false)); TextLayout layout = new TextLayout(tickLabel, getChartPainter().getStyleManager().getAxisTickLabelsFont(), frc); Rectangle2D tickLabelBounds = layout.getBounds(); - layout.draw(g, (float) xOffset, (float) (yOffset + axisTick.getAxis().getPaintZone().getHeight() - tickLocation + tickLabelBounds.getHeight() / 2.0)); + + Shape shape = layout.getOutline(null); + + AffineTransform orig = g.getTransform(); + AffineTransform at = new AffineTransform(); + at.translate(xOffset, flippedTickLocation + tickLabelBounds.getHeight() / 2.0); + g.transform(at); + g.fill(shape); + g.setTransform(orig); if (tickLabelBounds.getWidth() > maxTickLabelWidth) { maxTickLabelWidth = tickLabelBounds.getWidth(); } - // g.setTransform(orig); } } // bounds - bounds = new Rectangle2D.Double(xOffset, yOffset, maxTickLabelWidth, axisTick.getAxis().getPaintZone().getHeight()); + bounds = new Rectangle2D.Double(xOffset, yOffset, maxTickLabelWidth, height); // g.setColor(Color.blue); // g.draw(bounds); @@ -96,17 +102,36 @@ public class AxisTickLabels implements ChartPart { double xOffset = axisTick.getAxis().getPaintZone().getX(); double yOffset = axisTick.getAxis().getAxisTitle().getBounds().getY(); + double width = axisTick.getAxis().getPaintZone().getWidth(); double maxTickLabelHeight = 0; + + // System.out.println("axisTick.getTickLabels().size(): " + axisTick.getTickLabels().size()); for (int i = 0; i < axisTick.getTickLabels().size(); i++) { String tickLabel = axisTick.getTickLabels().get(i); + // System.out.println("tickLabel: " + tickLabel); double tickLocation = axisTick.getTickLocations().get(i); + double shiftedTickLocation = xOffset + tickLocation; + + if (tickLabel != null && shiftedTickLocation > xOffset && shiftedTickLocation < xOffset + width) { // some are null for logarithmic axes - if (tickLabel != null) { // some are null for logarithmic axes FontRenderContext frc = g.getFontRenderContext(); - TextLayout layout = new TextLayout(tickLabel, getChartPainter().getStyleManager().getAxisTickLabelsFont(), frc); - Rectangle2D tickLabelBounds = layout.getBounds(); - layout.draw(g, (float) (xOffset + tickLocation - tickLabelBounds.getWidth() / 2.0), (float) yOffset); + TextLayout textLayout = new TextLayout(tickLabel, getChartPainter().getStyleManager().getAxisTickLabelsFont(), frc); + + // Shape shape = v.getOutline(); + Shape shape = textLayout.getOutline(null); + Rectangle2D tickLabelBounds = shape.getBounds2D(); + + AffineTransform orig = g.getTransform(); + AffineTransform at = new AffineTransform(); + at.translate(shiftedTickLocation - tickLabelBounds.getWidth() / 2.0, yOffset); + g.transform(at); + g.fill(shape); + g.setTransform(orig); + + // // debug box + // g.setColor(Color.blue); + // g.draw(new Rectangle2D.Double(xOffset + tickLocation - tickLabelBounds.getWidth() / 2.0, yOffset - tickLabelBounds.getHeight(), tickLabelBounds.getWidth(), tickLabelBounds.getHeight())); if (tickLabelBounds.getHeight() > maxTickLabelHeight) { maxTickLabelHeight = tickLabelBounds.getHeight(); @@ -115,7 +140,7 @@ public class AxisTickLabels implements ChartPart { } // bounds - bounds = new Rectangle2D.Double(xOffset, yOffset - maxTickLabelHeight, axisTick.getAxis().getPaintZone().getWidth(), maxTickLabelHeight); + bounds = new Rectangle2D.Double(xOffset, yOffset - maxTickLabelHeight, width, maxTickLabelHeight); // g.setColor(Color.blue); // g.draw(bounds); diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickLogarithmicCalculator.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickLogarithmicCalculator.java index 4f8146a9e8d835939ccff04e477ffed1eece72df..60bb7358802b9a5fedbe43dc794f1a559f4dbfc5 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickLogarithmicCalculator.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickLogarithmicCalculator.java @@ -15,13 +15,15 @@ */ package com.xeiam.xchart.internal.chartpart; +import java.math.BigDecimal; + import com.xeiam.xchart.StyleManager; import com.xeiam.xchart.internal.Utils; import com.xeiam.xchart.internal.chartpart.Axis.Direction; /** * This class encapsulates the logic to generate the axis tick mark and axis tick label data for rendering the axis ticks for logarithmic axes - * + * * @author timmolter */ public class AxisTickLogarithmicCalculator extends AxisTickCalculator { @@ -30,14 +32,14 @@ public class AxisTickLogarithmicCalculator extends AxisTickCalculator { /** * Constructor - * + * * @param axisDirection * @param workingSpace * @param minValue * @param maxValue * @param styleManager */ - public AxisTickLogarithmicCalculator(Direction axisDirection, int workingSpace, double minValue, double maxValue, StyleManager styleManager) { + public AxisTickLogarithmicCalculator(Direction axisDirection, double workingSpace, double minValue, double maxValue, StyleManager styleManager) { super(axisDirection, workingSpace, minValue, maxValue, styleManager); numberFormatter = new NumberFormatter(styleManager); @@ -48,7 +50,7 @@ public class AxisTickLogarithmicCalculator extends AxisTickCalculator { // a check if all axis data are the exact same values if (minValue == maxValue) { - tickLabels.add(numberFormatter.formatNumber(maxValue)); + tickLabels.add(numberFormatter.formatNumber(BigDecimal.valueOf(maxValue), minValue, maxValue)); tickLocations.add(workingSpace / 2.0); return; } @@ -93,25 +95,25 @@ public class AxisTickLogarithmicCalculator extends AxisTickCalculator { // System.out.println("i: " + i); // System.out.println("pow(10, i).doubleValue(): " + pow(10, i).doubleValue()); - // using trhe .00000001 factor to dal with double value imprecision + // using the .00000001 factor to deal with double value imprecision for (double j = firstPosition; j <= Utils.pow(10, i) + .00000001; j = j + tickStep) { // System.out.println("j: " + j); // System.out.println(Math.log10(j) % 1); - if (j < minValue) { + if (j < minValue - tickStep) { // System.out.println("continue"); continue; } - if (j > maxValue) { + if (j > maxValue + tickStep) { // System.out.println("break"); break; } // only add labels for the decades if (Math.abs(Math.log10(j) % 1) < 0.00000001) { - tickLabels.add(numberFormatter.formatNumber(j)); + tickLabels.add(numberFormatter.formatLogNumber(j)); } else { tickLabels.add(null); diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickMarks.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickMarks.java index 9dc312310dfbc8aebc6b3cec8e6d9874d1b33a18..6d913bf40e6d7d0fb0ea3321a1eefe95303512f0 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickMarks.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickMarks.java @@ -33,7 +33,7 @@ public class AxisTickMarks implements ChartPart { /** * Constructor - * + * * @param axisTick */ protected AxisTickMarks(AxisTick axisTick) { @@ -58,20 +58,26 @@ public class AxisTickMarks implements ChartPart { double xOffset = axisTick.getAxisTickLabels().getBounds().getX() + axisTick.getAxisTickLabels().getBounds().getWidth() + getChartPainter().getStyleManager().getAxisTickPadding(); double yOffset = axisTick.getAxis().getPaintZone().getY(); + // bounds + bounds = new Rectangle2D.Double(xOffset, yOffset, getChartPainter().getStyleManager().getAxisTickMarkLength(), axisTick.getAxis().getPaintZone().getHeight()); + // g.setColor(Color.yellow); + // g.draw(bounds); + // tick marks if (getChartPainter().getStyleManager().isAxisTicksMarksVisible()) { for (int i = 0; i < axisTick.getTickLabels().size(); i++) { double tickLocation = axisTick.getTickLocations().get(i); + double flippedTickLocation = yOffset + axisTick.getAxis().getPaintZone().getHeight() - tickLocation; + if (flippedTickLocation > bounds.getY() && flippedTickLocation < bounds.getY() + bounds.getHeight()) { - Shape line = - new Line2D.Double(xOffset, yOffset + axisTick.getAxis().getPaintZone().getHeight() - tickLocation, xOffset + getChartPainter().getStyleManager().getAxisTickMarkLength(), yOffset - + axisTick.getAxis().getPaintZone().getHeight() - tickLocation); - g.draw(line); - + Shape line = new Line2D.Double(xOffset, flippedTickLocation, xOffset + getChartPainter().getStyleManager().getAxisTickMarkLength(), flippedTickLocation); + g.draw(line); + } } } + // Line if (getChartPainter().getStyleManager().isAxisTicksLineVisible()) { @@ -82,28 +88,35 @@ public class AxisTickMarks implements ChartPart { } - // bounds - bounds = new Rectangle2D.Double(xOffset, yOffset, getChartPainter().getStyleManager().getAxisTickMarkLength(), axisTick.getAxis().getPaintZone().getHeight()); - // g.setColor(Color.yellow); - // g.draw(bounds); - } else if (axisTick.getAxis().getDirection() == Axis.Direction.X && getChartPainter().getStyleManager().isXAxisTicksVisible()) { // X-Axis double xOffset = axisTick.getAxis().getPaintZone().getX(); double yOffset = axisTick.getAxisTickLabels().getBounds().getY() - getChartPainter().getStyleManager().getAxisTickPadding(); + // bounds + bounds = + new Rectangle2D.Double(xOffset, yOffset - getChartPainter().getStyleManager().getAxisTickMarkLength(), axisTick.getAxis().getPaintZone().getWidth(), getChartPainter().getStyleManager() + .getAxisTickMarkLength()); + // g.setColor(Color.yellow); + // g.draw(bounds); + // tick marks if (getChartPainter().getStyleManager().isAxisTicksMarksVisible()) { for (int i = 0; i < axisTick.getTickLabels().size(); i++) { double tickLocation = axisTick.getTickLocations().get(i); + double shiftedTickLocation = xOffset + tickLocation; + + if (shiftedTickLocation > bounds.getX() && shiftedTickLocation < bounds.getX() + bounds.getWidth()) { - Shape line = new Line2D.Double(xOffset + tickLocation, yOffset, xOffset + tickLocation, yOffset - getChartPainter().getStyleManager().getAxisTickMarkLength()); - g.draw(line); + Shape line = new Line2D.Double(shiftedTickLocation, yOffset, xOffset + tickLocation, yOffset - getChartPainter().getStyleManager().getAxisTickMarkLength()); + g.draw(line); + } } } + // Line if (getChartPainter().getStyleManager().isAxisTicksLineVisible()) { @@ -113,12 +126,6 @@ public class AxisTickMarks implements ChartPart { g.draw(line); } - // bounds - bounds = - new Rectangle2D.Double(xOffset, yOffset - getChartPainter().getStyleManager().getAxisTickMarkLength(), axisTick.getAxis().getPaintZone().getWidth(), getChartPainter().getStyleManager() - .getAxisTickMarkLength()); - // g.setColor(Color.yellow); - // g.draw(bounds); } } diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickNumericalCalculator.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickNumericalCalculator.java index 58a1aa275741ecd1d312c09b193a2c8adf39d0d1..dffe6f11d5e6f499460ef3e1a555c813983fbee2 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickNumericalCalculator.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickNumericalCalculator.java @@ -16,6 +16,7 @@ package com.xeiam.xchart.internal.chartpart; import java.math.BigDecimal; +import java.math.RoundingMode; import com.xeiam.xchart.StyleManager; import com.xeiam.xchart.internal.Utils; @@ -23,7 +24,7 @@ import com.xeiam.xchart.internal.chartpart.Axis.Direction; /** * This class encapsulates the logic to generate the axis tick mark and axis tick label data for rendering the axis ticks for decimal axes - * + * * @author timmolter */ public class AxisTickNumericalCalculator extends AxisTickCalculator { @@ -32,14 +33,14 @@ public class AxisTickNumericalCalculator extends AxisTickCalculator { /** * Constructor - * + * * @param axisDirection * @param workingSpace * @param minValue * @param maxValue * @param styleManager */ - public AxisTickNumericalCalculator(Direction axisDirection, int workingSpace, double minValue, double maxValue, StyleManager styleManager) { + public AxisTickNumericalCalculator(Direction axisDirection, double workingSpace, double minValue, double maxValue, StyleManager styleManager) { super(axisDirection, workingSpace, minValue, maxValue, styleManager); numberFormatter = new NumberFormatter(styleManager); @@ -50,7 +51,7 @@ public class AxisTickNumericalCalculator extends AxisTickCalculator { // a check if all axis data are the exact same values if (minValue == maxValue) { - tickLabels.add(numberFormatter.formatNumber(maxValue)); + tickLabels.add(numberFormatter.formatNumber(BigDecimal.valueOf(maxValue), minValue, maxValue)); tickLocations.add(workingSpace / 2.0); return; } @@ -62,12 +63,18 @@ public class AxisTickNumericalCalculator extends AxisTickCalculator { double margin = Utils.getTickStartOffset(workingSpace, tickSpace); // in plot space double gridStep = getGridStepForDecimal(tickSpace); BigDecimal gridStep = BigDecimal.valueOf(getNumericalGridStep(tickSpace)); - BigDecimal firstPosition = BigDecimal.valueOf(getFirstPosition(gridStep.doubleValue())); + // System.out.println("***gridStep: " + gridStep); + BigDecimal cleanedGridStep = gridStep.setScale(16, RoundingMode.HALF_UP); // chop off any double imprecision + // System.out.println("cleanedGridStep: " + cleanedGridStep); + BigDecimal firstPosition = BigDecimal.valueOf(getFirstPosition(cleanedGridStep.doubleValue())); + // System.out.println("firstPosition: " + firstPosition); // chop off any double imprecision + BigDecimal cleanedFirstPosition = firstPosition.setScale(16, RoundingMode.HALF_UP); // chop off any double imprecision + // System.out.println("scaledfirstPosition: " + cleanedFirstPosition); // generate all tickLabels and tickLocations from the first to last position - for (BigDecimal tickPosition = firstPosition; tickPosition.compareTo(BigDecimal.valueOf(maxValue)) <= 0; tickPosition = tickPosition.add(gridStep)) { + for (BigDecimal tickPosition = cleanedFirstPosition; tickPosition.compareTo(BigDecimal.valueOf(maxValue + 2 * cleanedGridStep.doubleValue())) < 0; tickPosition = tickPosition.add(cleanedGridStep)) { - tickLabels.add(numberFormatter.formatNumber(tickPosition.doubleValue())); + tickLabels.add(numberFormatter.formatNumber(tickPosition, minValue, maxValue)); // here we convert tickPosition finally to plot space, i.e. pixels double tickLabelPosition = margin + ((tickPosition.doubleValue() - minValue) / (maxValue - minValue) * tickSpace); tickLocations.add(tickLabelPosition); 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 c3f2be213da3b6de8650795632296323217f2f6a..e16c4a7d2cb60dd2f758a7e919260719fdccb1eb 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 @@ -16,6 +16,7 @@ package com.xeiam.xchart.internal.chartpart; import java.awt.Graphics2D; +import java.awt.Shape; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; @@ -72,11 +73,13 @@ public class AxisTitle implements ChartPart { 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)); + AffineTransform at = new AffineTransform(); + at.rotate(Math.PI / -2.0, xOffset, yOffset); + g.transform(at); g.drawString(text, xOffset, yOffset); + g.setTransform(orig); // /////////////////////////////////////////////// - g.setTransform(orig); // System.out.println(nonRotatedRectangle.getHeight()); // bounds @@ -103,7 +106,14 @@ public class AxisTitle implements ChartPart { double xOffset = axis.getPaintZone().getX() + (axis.getPaintZone().getWidth() - rectangle.getWidth()) / 2.0; double yOffset = axis.getPaintZone().getY() + axis.getPaintZone().getHeight() - rectangle.getHeight(); - textLayout.draw(g, (float) xOffset, (float) (yOffset - rectangle.getY())); + // textLayout.draw(g, (float) xOffset, (float) (yOffset - rectangle.getY())); + Shape shape = textLayout.getOutline(null); + AffineTransform orig = g.getTransform(); + AffineTransform at = new AffineTransform(); + at.translate((float) xOffset, (float) (yOffset - rectangle.getY())); + g.transform(at); + g.fill(shape); + g.setTransform(orig); bounds = new Rectangle2D.Double(xOffset, yOffset - getChartPainter().getStyleManager().getAxisTitlePadding(), rectangle.getWidth(), rectangle.getHeight() diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/ChartPainter.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/ChartPainter.java index ccc1e09db98bca490120d4e092cdf6fb625c5174..dcc231fe169dee18fec55e04db62988f56c623fb 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/ChartPainter.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/ChartPainter.java @@ -41,7 +41,7 @@ public class ChartPainter { /** * Constructor - * + * * @param width * @param height */ @@ -117,7 +117,7 @@ public class ChartPainter { /** * for internal usage - * + * * @return */ public ChartTitle getChartTitle() { @@ -127,7 +127,7 @@ public class ChartPainter { /** * for internal usage - * + * * @return */ public Legend getChartLegend() { @@ -137,7 +137,7 @@ public class ChartPainter { /** * for internal usage - * + * * @return */ public AxisPair getAxisPair() { @@ -147,7 +147,7 @@ public class ChartPainter { /** * for internal usage - * + * * @return */ public Plot getPlot() { @@ -167,7 +167,7 @@ public class ChartPainter { /** * Gets the Chart's style manager, which can be used to customize the Chart's appearance - * + * * @return the style manager */ public StyleManager getStyleManager() { 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 f7126897681432de2a81935222f5535e40428e54..973ff52a9d50c184a5f7548bebe0c184b14cdab4 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.Shape; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; /** @@ -120,7 +121,15 @@ public class ChartTitle implements ChartPart { // g.draw(bounds); g.setColor(chartPainter.getStyleManager().getChartFontColor()); - textLayout.draw(g, xOffset, yOffset); + // textLayout.draw(g, xOffset, yOffset); + + Shape shape = textLayout.getOutline(null); + AffineTransform orig = g.getTransform(); + AffineTransform at = new AffineTransform(); + at.translate(xOffset, yOffset); + g.transform(at); + g.fill(shape); + g.setTransform(orig); } } diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/DateFormatter.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/DateFormatter.java index db0dd73b601a5f8bb2a6879522bd91bc596cf3af..50bbb0d1d56eafa0ddc2126860b9f87e32685154 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/DateFormatter.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/DateFormatter.java @@ -88,6 +88,7 @@ public class DateFormatter { String datePattern; if (styleManager.getDatePattern() == null) { + // intelligently set date pattern if none is given if (timeUnit == MILLIS_SCALE) { datePattern = "ss.SSS"; 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 1d0f74a843fa91cb4872ecee1f99ac1cb0525b01..d07fc1b46bcc5334cf385f03bef81fe175bebd1e 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 @@ -16,20 +16,19 @@ package com.xeiam.xchart.internal.chartpart; import java.awt.BasicStroke; -import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.List; +import java.util.LinkedHashMap; import java.util.Map; import com.xeiam.xchart.Series; import com.xeiam.xchart.StyleManager; import com.xeiam.xchart.StyleManager.ChartType; -import com.xeiam.xchart.internal.markers.Marker; /** * @author timmolter @@ -38,6 +37,7 @@ public class Legend implements ChartPart { private static final int LEGEND_MARGIN = 6; private static final int BOX_SIZE = 20; + private static final int MULTI_LINE_SPACE = 3; /** * parent @@ -72,34 +72,30 @@ public class Legend implements ChartPart { } StyleManager styleManager = getChartPainter().getStyleManager(); - FontMetrics fontMetrics = g.getFontMetrics(chartPainter.getStyleManager().getLegendFont()); boolean isBar = styleManager.getChartType() == ChartType.Bar; // determine legend text content max width double legendTextContentMaxWidth = 0; - double legendTextContentMaxHeight = 0; // determine legend content height double legendContentHeight = 0; for (Series series : chartPainter.getAxisPair().getSeriesMap().values()) { - List<Map.Entry<String, Rectangle2D>> seriesBounds = getSeriesBounds(series, g); + + Map<String, Rectangle2D> seriesBounds = getSeriesTextBounds(series, g); + double blockHeight = 0; - for (Map.Entry<String, Rectangle2D> entry : seriesBounds) { - blockHeight += entry.getValue().getHeight(); + for (Map.Entry<String, Rectangle2D> entry : seriesBounds.entrySet()) { + blockHeight += entry.getValue().getHeight() + MULTI_LINE_SPACE; legendTextContentMaxWidth = Math.max(legendTextContentMaxWidth, entry.getValue().getWidth()); } - blockHeight = Math.max(blockHeight, isBar ? BOX_SIZE : Marker.SIZE); + blockHeight -= MULTI_LINE_SPACE; + blockHeight = Math.max(blockHeight, isBar ? BOX_SIZE : getChartPainter().getStyleManager().getMarkerSize()); - legendTextContentMaxHeight = Math.max(legendTextContentMaxHeight, blockHeight); - legendContentHeight += blockHeight; + legendContentHeight += blockHeight + styleManager.getLegendPadding(); } - // vertical padding between items - double paddingSize = isBar ? styleManager.getLegendPadding() : fontMetrics.getDescent(); - legendContentHeight += paddingSize * (chartPainter.getAxisPair().getSeriesMap().size() - 1); - // determine legend content width double legendContentWidth = 0; if (!isBar) { @@ -108,41 +104,30 @@ public class Legend implements ChartPart { else { legendContentWidth = BOX_SIZE + styleManager.getLegendPadding() + legendTextContentMaxWidth; } + // Legend Box double legendBoxWidth = legendContentWidth + 2 * styleManager.getLegendPadding(); - double legendBoxHeight = legendContentHeight + 2 * styleManager.getLegendPadding(); + double legendBoxHeight = legendContentHeight + 1 * styleManager.getLegendPadding(); return new double[] { legendBoxWidth, legendBoxHeight }; } - private List<Map.Entry<String, Rectangle2D>> getSeriesBounds(Series series, Graphics2D g) { - - String lines[] = series.getName().split("\\n"); - List<Map.Entry<String, Rectangle2D>> stringBounds = new ArrayList<Map.Entry<String, Rectangle2D>>(lines.length); - for (String line : lines) { - Rectangle2D bounds = g.getFontMetrics().getStringBounds(line, g); - stringBounds.add(new AbstractMap.SimpleEntry<String, Rectangle2D>(line, bounds)); - } - return stringBounds; - } - @Override public void paint(Graphics2D g) { bounds = new Rectangle2D.Double(); - g.setFont(chartPainter.getStyleManager().getLegendFont()); + // g.setFont(chartPainter.getStyleManager().getLegendFont()); StyleManager styleManager = getChartPainter().getStyleManager(); - if (!styleManager.isLegendVisible()) + if (!styleManager.isLegendVisible()) { return; + } final double[] sizeHint = getSizeHint(g); double legendBoxWidth = sizeHint[0]; double legendBoxHeight = sizeHint[1]; - FontMetrics fontMetrics = g.getFontMetrics(styleManager.getLegendFont()); - // legend draw position double xOffset = 0; double yOffset = 0; @@ -188,12 +173,21 @@ public class Legend implements ChartPart { double starty = yOffset + styleManager.getLegendPadding(); for (Series series : chartPainter.getAxisPair().getSeriesMap().values()) { - List<Map.Entry<String, Rectangle2D>> seriesBounds = getSeriesBounds(series, g); + + Map<String, Rectangle2D> seriesTextBounds = getSeriesTextBounds(series, g); + float blockHeight = 0; - for (Map.Entry<String, Rectangle2D> entry : seriesBounds) - blockHeight += entry.getValue().getHeight(); + double legendTextContentMaxWidth = 0; + for (Map.Entry<String, Rectangle2D> entry : seriesTextBounds.entrySet()) { + blockHeight += entry.getValue().getHeight() + MULTI_LINE_SPACE; + legendTextContentMaxWidth = Math.max(legendTextContentMaxWidth, entry.getValue().getWidth()); + } + blockHeight -= MULTI_LINE_SPACE; + + blockHeight = Math.max(blockHeight, styleManager.getChartType() == ChartType.Bar ? BOX_SIZE : getChartPainter().getStyleManager().getMarkerSize()); if (styleManager.getChartType() != ChartType.Bar) { + // paint line if (styleManager.getChartType() != ChartType.Scatter && series.getStroke() != null) { g.setColor(series.getStrokeColor()); @@ -202,10 +196,17 @@ public class Legend implements ChartPart { g.draw(line); } + // // debug box + // Rectangle2D boundsTemp = new Rectangle2D.Double(startx, starty, styleManager.getLegendSeriesLineLength(), blockHeight); + // g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + // g.setColor(Color.red); + // g.draw(boundsTemp); + // paint marker if (series.getMarker() != null) { g.setColor(series.getMarkerColor()); - series.getMarker().paint(g, startx + styleManager.getLegendSeriesLineLength() / 2.0, starty + blockHeight / 2.0); + series.getMarker().paint(g, startx + styleManager.getLegendSeriesLineLength() / 2.0, starty + blockHeight / 2.0, getChartPainter().getStyleManager().getMarkerSize()); + } } else { @@ -215,30 +216,72 @@ public class Legend implements ChartPart { Shape rectSmall = new Rectangle2D.Double(startx, starty, BOX_SIZE, BOX_SIZE); g.fill(rectSmall); } + // // debug box + // Rectangle2D boundsTemp = new Rectangle2D.Double(startx, starty, BOX_SIZE, BOX_SIZE); + // g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + // g.setColor(Color.red); + // g.draw(boundsTemp); } - // paint series name + // paint series text g.setColor(chartPainter.getStyleManager().getChartFontColor()); - float itemOffsetY = -fontMetrics.getDescent(); + double multiLineOffset = 0.0; + if (styleManager.getChartType() != ChartType.Bar) { - final float x = (float) (startx + styleManager.getLegendSeriesLineLength() + styleManager.getLegendPadding()); - for (Map.Entry<String, Rectangle2D> entry : seriesBounds) { - g.drawString(entry.getKey(), x, (float) (starty + entry.getValue().getHeight()) + itemOffsetY); - itemOffsetY += entry.getValue().getHeight(); + + double x = startx + styleManager.getLegendSeriesLineLength() + styleManager.getLegendPadding(); + for (Map.Entry<String, Rectangle2D> entry : seriesTextBounds.entrySet()) { + + double height = entry.getValue().getHeight(); + double centerOffsetY = (Math.max(getChartPainter().getStyleManager().getMarkerSize(), height) - height) / 2.0; + + FontRenderContext frc = g.getFontRenderContext(); + TextLayout tl = new TextLayout(entry.getKey(), styleManager.getLegendFont(), frc); + Shape shape = tl.getOutline(null); + AffineTransform orig = g.getTransform(); + AffineTransform at = new AffineTransform(); + at.translate(x, starty + height + centerOffsetY + multiLineOffset); + g.transform(at); + g.fill(shape); + g.setTransform(orig); + + // // debug box + // Rectangle2D boundsTemp = new Rectangle2D.Double(x, starty + centerOffsetY + multiLineOffset, entry.getValue().getWidth(), height); + // g.setColor(Color.blue); + // g.draw(boundsTemp); + + multiLineOffset += height + MULTI_LINE_SPACE; } - itemOffsetY = (float) Math.max(itemOffsetY, Marker.SIZE); - starty += blockHeight + fontMetrics.getDescent(); + + starty += blockHeight + styleManager.getLegendPadding(); } else { - final float x = (float) (startx + BOX_SIZE + styleManager.getLegendPadding()); - for (Map.Entry<String, Rectangle2D> entry : seriesBounds) { + + final double x = startx + BOX_SIZE + styleManager.getLegendPadding(); + for (Map.Entry<String, Rectangle2D> entry : seriesTextBounds.entrySet()) { + double height = entry.getValue().getHeight(); double centerOffsetY = (Math.max(BOX_SIZE, height) - height) / 2.0; - g.drawString(entry.getKey(), x, (float) (starty + height + itemOffsetY + centerOffsetY)); - itemOffsetY += height; + + FontRenderContext frc = g.getFontRenderContext(); + TextLayout tl = new TextLayout(entry.getKey(), styleManager.getLegendFont(), frc); + Shape shape = tl.getOutline(null); + AffineTransform orig = g.getTransform(); + AffineTransform at = new AffineTransform(); + at.translate(x, starty + height + centerOffsetY + multiLineOffset); + g.transform(at); + g.fill(shape); + g.setTransform(orig); + + // // debug box + // Rectangle2D boundsTemp = new Rectangle2D.Double(x, starty + centerOffsetY, entry.getValue().getWidth(), height); + // g.setColor(Color.blue); + // g.draw(boundsTemp); + multiLineOffset += height + MULTI_LINE_SPACE; + } - starty += Math.max(BOX_SIZE, blockHeight) + styleManager.getLegendPadding(); + starty += blockHeight + styleManager.getLegendPadding(); } } @@ -251,6 +294,28 @@ public class Legend implements ChartPart { } + private Map<String, Rectangle2D> getSeriesTextBounds(Series series, Graphics2D g) { + + // FontMetrics fontMetrics = g.getFontMetrics(getChartPainter().getStyleManager().getLegendFont()); + // float fontDescent = fontMetrics.getDescent(); + + String lines[] = series.getName().split("\\n"); + Map<String, Rectangle2D> seriesTextBounds = new LinkedHashMap<String, Rectangle2D>(lines.length); + for (String line : lines) { + FontRenderContext frc = g.getFontRenderContext(); + TextLayout tl = new TextLayout(line, getChartPainter().getStyleManager().getLegendFont(), frc); + Shape shape = tl.getOutline(null); + Rectangle2D bounds = shape.getBounds2D(); + // System.out.println(tl.getAscent()); + // System.out.println(tl.getDescent()); + // System.out.println(tl.getBounds()); + // seriesTextBounds.put(line, new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight() - tl.getDescent())); + // seriesTextBounds.put(line, new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), tl.getAscent())); + seriesTextBounds.put(line, bounds); + } + return seriesTextBounds; + } + @Override public Rectangle2D getBounds() { diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/NumberFormatter.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/NumberFormatter.java index 7387214e9551d02e5b4ce1e143a7c9c3984fa4e1..5685df93065350addc032e5ccf409050d174ba97 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/NumberFormatter.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/NumberFormatter.java @@ -15,6 +15,7 @@ */ package com.xeiam.xchart.internal.chartpart; +import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.NumberFormat; @@ -35,32 +36,122 @@ public class NumberFormatter { this.styleManager = styleManager; } + public String getFormatPattern(BigDecimal value, double min, double max) { + + // System.out.println("value: " + value); + // System.out.println("min: " + min); + // System.out.println("max: " + max); + + double difference = max - min; + int placeOfDifference; + if (difference == 0.0) { + placeOfDifference = 0; + } + else { + placeOfDifference = (int) Math.floor(Math.log(difference) / Math.log(10)); + } + int placeOfValue; + if (value.doubleValue() == 0.0) { + placeOfValue = 0; + } + else { + placeOfValue = (int) Math.floor(Math.log(value.doubleValue()) / Math.log(10)); + } + + // System.out.println("difference: " + difference); + // System.out.println("placeOfDifference: " + placeOfDifference); + // System.out.println("placeOfValue: " + placeOfValue); + + if (placeOfDifference <= 4 && placeOfDifference >= -4) { + // System.out.println("getNormalDecimalPattern"); + return getNormalDecimalPatternPositive(placeOfValue, placeOfDifference); + } + else { + // System.out.println("getScientificDecimalPattern"); + return getScientificDecimalPattern(); + } + } + + private String getNormalDecimalPatternPositive(int placeOfValue, int placeOfDifference) { + + int maxNumPlaces = 15; + StringBuilder sb = new StringBuilder(); + for (int i = maxNumPlaces - 1; i >= -1 * maxNumPlaces; i--) { + + if (i >= 0 && (i < placeOfValue)) { + sb.append("0"); + } + else if (i < 0 && (i > placeOfValue)) { + sb.append("0"); + } + else { + sb.append("#"); + } + if (i % 3 == 0 && i > 0) { + sb.append(","); + } + if (i == 0) { + sb.append("."); + } + } + // System.out.println(sb.toString()); + return sb.toString(); + } + + private String getScientificDecimalPattern() { + + return "0.###############E0"; + } + /** * Format a number value, if the override patterns are null, it uses defaults - * + * * @param value * @return */ - public String formatNumber(double value) { + public String formatNumber(BigDecimal value, double min, double max) { NumberFormat numberFormat = NumberFormat.getNumberInstance(styleManager.getLocale()); - double absoluteValue = Math.abs(value); + String decimalPattern; - if (absoluteValue < 10000.000001 && absoluteValue > .0009999999 || value == 0) { - - DecimalFormat normalFormat = (DecimalFormat) numberFormat; - normalFormat.applyPattern(styleManager.getNormalDecimalPattern()); - return normalFormat.format(value); + if (styleManager.getDecimalPattern() == null) { + decimalPattern = getFormatPattern(value, min, max); } else { + decimalPattern = styleManager.getDecimalPattern(); + } - DecimalFormat scientificFormat = (DecimalFormat) numberFormat; - scientificFormat.applyPattern(styleManager.getScientificDecimalPattern()); - return scientificFormat.format(value); + DecimalFormat normalFormat = (DecimalFormat) numberFormat; + normalFormat.applyPattern(decimalPattern); + return normalFormat.format(value); + } + + /** + * Format a log number value for log Axes which show only decade tick labels. if the override patterns are null, it uses defaults + * + * @param value + * @return + */ + public String formatLogNumber(double value) { + + NumberFormat numberFormat = NumberFormat.getNumberInstance(styleManager.getLocale()); + + String decimalPattern; + + if (styleManager.getDecimalPattern() == null) { + + decimalPattern = "0E0"; + } + else { + decimalPattern = styleManager.getDecimalPattern(); } + DecimalFormat normalFormat = (DecimalFormat) numberFormat; + normalFormat.applyPattern(decimalPattern); + return normalFormat.format(value); + } } diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/PlotContentBarChart.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/PlotContentBarChart.java index 318e793c51f523ea17483a561aaf39cad05152b5..a13983f83ee28ee2c00f5f3109eae1503c6fe7d8 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/PlotContentBarChart.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/PlotContentBarChart.java @@ -62,7 +62,7 @@ public class PlotContentBarChart extends PlotContent { for (Series series : getChartPainter().getAxisPair().getSeriesMap().values()) { // data points - Collection<?> xData = series.getXData(); + // Collection<?> xData = series.getXData(); Collection<? extends Number> yData = series.getYData(); double yMin = getChartPainter().getAxisPair().getYAxis().getMin(); @@ -170,7 +170,7 @@ public class PlotContentBarChart extends PlotContent { boolean isOverlap = true; double xOffset; double barWidth; - if (getChartPainter().getStyleManager().barsOverlapped()) { + if (getChartPainter().getStyleManager().isBarsOverlapped()) { double barWidthPercentage = getChartPainter().getStyleManager().getBarWidthPercentage(); barWidth = gridStep * barWidthPercentage; double barMargin = gridStep * (1 - barWidthPercentage) / 2; diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/PlotContentLineChart.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/PlotContentLineChart.java index d08eebb7f98c40cf9e027dbf14ff8f7e5d3ef69e..27f39f03e0891288555d54924c81ad0678683185 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/PlotContentLineChart.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/PlotContentLineChart.java @@ -37,7 +37,7 @@ public class PlotContentLineChart extends PlotContent { /** * Constructor - * + * * @param plot */ protected PlotContentLineChart(Plot plot) { @@ -66,6 +66,7 @@ public class PlotContentLineChart extends PlotContent { // data points Collection<?> xData = series.getXData(); + // System.out.println(xData); double xMin = getChartPainter().getAxisPair().getXAxis().getMin(); double xMax = getChartPainter().getAxisPair().getXAxis().getMax(); @@ -205,7 +206,7 @@ public class PlotContentLineChart extends PlotContent { // paint marker if (series.getMarker() != null) { g.setColor(series.getMarkerColor()); - series.getMarker().paint(g, xOffset, yOffset); + series.getMarker().paint(g, xOffset, yOffset, getChartPainter().getStyleManager().getMarkerSize()); } // paint errorbars diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/PlotSurface.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/PlotSurface.java index 5580d85bc1e92315a9276d4aa5901fd90e7a138a..c36dbebba04db8774bc34c5957d8fb54d2837ac7 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/PlotSurface.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/PlotSurface.java @@ -24,6 +24,8 @@ import java.util.List; import com.xeiam.xchart.StyleManager.ChartType; /** + * Draws the plot background, the plot border and the horizontal and vertical grid lines + * * @author timmolter */ public class PlotSurface implements ChartPart { @@ -33,7 +35,7 @@ public class PlotSurface implements ChartPart { /** * Constructor - * + * * @param plot */ protected PlotSurface(Plot plot) { @@ -70,26 +72,28 @@ public class PlotSurface implements ChartPart { List<Double> yAxisTickLocations = getChartPainter().getAxisPair().getYAxis().getAxisTick().getTickLocations(); for (int i = 0; i < yAxisTickLocations.size(); i++) { - double tickLocation = yAxisTickLocations.get(i); - double yOffset = bounds.getY() + bounds.getHeight() - tickLocation; + double yOffset = bounds.getY() + bounds.getHeight() - yAxisTickLocations.get(i); - // draw lines - if (getChartPainter().getStyleManager().isPlotGridLinesVisible()) { + if (yOffset > bounds.getY() && yOffset < bounds.getY() + bounds.getHeight()) { - g.setColor(getChartPainter().getStyleManager().getPlotGridLinesColor()); - g.setStroke(getChartPainter().getStyleManager().getPlotGridLinesStroke()); - Shape line = new Line2D.Double(bounds.getX(), yOffset, bounds.getX() + bounds.getWidth(), yOffset); - g.draw(line); - } - // tick marks - if (getChartPainter().getStyleManager().isPlotTicksMarksVisible()) { - - g.setColor(getChartPainter().getStyleManager().getAxisTickMarksColor()); - g.setStroke(getChartPainter().getStyleManager().getAxisTickMarksStroke()); - Shape line = new Line2D.Double(bounds.getX(), yOffset, bounds.getX() + getChartPainter().getStyleManager().getAxisTickMarkLength(), yOffset); - g.draw(line); - line = new Line2D.Double(bounds.getX() + bounds.getWidth(), yOffset, bounds.getX() + bounds.getWidth() - getChartPainter().getStyleManager().getAxisTickMarkLength(), yOffset); - g.draw(line); + // draw lines + if (getChartPainter().getStyleManager().isPlotGridLinesVisible()) { + + g.setColor(getChartPainter().getStyleManager().getPlotGridLinesColor()); + g.setStroke(getChartPainter().getStyleManager().getPlotGridLinesStroke()); + Shape line = new Line2D.Double(bounds.getX(), yOffset, bounds.getX() + bounds.getWidth(), yOffset); + g.draw(line); + } + // tick marks + if (getChartPainter().getStyleManager().isPlotTicksMarksVisible()) { + + g.setColor(getChartPainter().getStyleManager().getAxisTickMarksColor()); + g.setStroke(getChartPainter().getStyleManager().getAxisTickMarksStroke()); + Shape line = new Line2D.Double(bounds.getX(), yOffset, bounds.getX() + getChartPainter().getStyleManager().getAxisTickMarkLength(), yOffset); + g.draw(line); + line = new Line2D.Double(bounds.getX() + bounds.getWidth(), yOffset, bounds.getX() + bounds.getWidth() - getChartPainter().getStyleManager().getAxisTickMarkLength(), yOffset); + g.draw(line); + } } } @@ -108,24 +112,27 @@ public class PlotSurface implements ChartPart { double tickLocation = xAxisTickLocations.get(i); double xOffset = bounds.getX() + tickLocation; - // draw lines - if (getChartPainter().getStyleManager().isPlotGridLinesVisible()) { - g.setColor(getChartPainter().getStyleManager().getPlotGridLinesColor()); - g.setStroke(getChartPainter().getStyleManager().getPlotGridLinesStroke()); + if (xOffset > bounds.getX() && xOffset < bounds.getX() + bounds.getWidth()) { - Shape line = new Line2D.Double(xOffset, bounds.getY(), xOffset, bounds.getY() + bounds.getHeight()); - g.draw(line); - } - // tick marks - if (getChartPainter().getStyleManager().isPlotTicksMarksVisible()) { + // draw lines + if (getChartPainter().getStyleManager().isPlotGridLinesVisible()) { + g.setColor(getChartPainter().getStyleManager().getPlotGridLinesColor()); + g.setStroke(getChartPainter().getStyleManager().getPlotGridLinesStroke()); - g.setColor(getChartPainter().getStyleManager().getAxisTickMarksColor()); - g.setStroke(getChartPainter().getStyleManager().getAxisTickMarksStroke()); + Shape line = new Line2D.Double(xOffset, bounds.getY(), xOffset, bounds.getY() + bounds.getHeight()); + g.draw(line); + } + // tick marks + if (getChartPainter().getStyleManager().isPlotTicksMarksVisible()) { - Shape line = new Line2D.Double(xOffset, bounds.getY(), xOffset, bounds.getY() + getChartPainter().getStyleManager().getAxisTickMarkLength()); - g.draw(line); - line = new Line2D.Double(xOffset, bounds.getY() + bounds.getHeight(), xOffset, bounds.getY() + bounds.getHeight() - getChartPainter().getStyleManager().getAxisTickMarkLength()); - g.draw(line); + g.setColor(getChartPainter().getStyleManager().getAxisTickMarksColor()); + g.setStroke(getChartPainter().getStyleManager().getAxisTickMarksStroke()); + + Shape line = new Line2D.Double(xOffset, bounds.getY(), xOffset, bounds.getY() + getChartPainter().getStyleManager().getAxisTickMarkLength()); + g.draw(line); + line = new Line2D.Double(xOffset, bounds.getY() + bounds.getHeight(), xOffset, bounds.getY() + bounds.getHeight() - getChartPainter().getStyleManager().getAxisTickMarkLength()); + g.draw(line); + } } } } 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 b3cd237d7cb2d3c483343b72a24f3519598638d6..489f7abf0d6efaa440ffc9f0cb97ee25c087dac2 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 @@ -25,10 +25,11 @@ import java.awt.geom.Ellipse2D; public class Circle extends Marker { @Override - public void paint(Graphics2D g, double xOffset, double yOffset) { + public void paint(Graphics2D g, double xOffset, double yOffset, int markerSize) { g.setStroke(stroke); - Shape circle = new Ellipse2D.Double(xOffset - Marker.HALF_SIZE, yOffset - Marker.HALF_SIZE, Marker.SIZE, Marker.SIZE); + double halfSize = (double) markerSize / 2; + Shape circle = new Ellipse2D.Double(xOffset - halfSize, yOffset - halfSize, markerSize, markerSize); g.fill(circle); } 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 4a5d0abc6bca936551f2587568dbb71c873ed3d1..78f9883ec50ca6387ba63403ea194678dacaa8fe 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 @@ -24,12 +24,12 @@ import java.awt.geom.Path2D; public class Diamond extends Marker { @Override - public void paint(Graphics2D g, double xOffset, double yOffset) { + public void paint(Graphics2D g, double xOffset, double yOffset, int markerSize) { g.setStroke(stroke); // Make a diamond - double diamondHalfSize = Marker.HALF_SIZE * 1.3; + double diamondHalfSize = (double) markerSize / 2 * 1.3; Path2D.Double path = new Path2D.Double(); path.moveTo(xOffset - diamondHalfSize, yOffset); 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 dd24d7f3ccdf6c4fa84b5ebf73decc17fb46b663..42ba4e3ca51cb57b29d619a716922295fc6901e3 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,9 +25,6 @@ public abstract class Marker { protected BasicStroke stroke = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); - public static final double SIZE = 8; + public abstract void paint(Graphics2D g, double xOffset, double yOffset, int markerSize); - public static final double HALF_SIZE = SIZE / 2.0; - - public abstract void paint(Graphics2D g, double xOffset, double 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 2848423b170797189b6b62cf7a6cd2af617d7def..f297ebdb6dabe501f56523b8a7a6084202014db1 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 @@ -25,10 +25,11 @@ import java.awt.geom.Rectangle2D; public class Square extends Marker { @Override - public void paint(Graphics2D g, double xOffset, double yOffset) { + public void paint(Graphics2D g, double xOffset, double yOffset, int markerSize) { g.setStroke(stroke); - Shape square = new Rectangle2D.Double(xOffset - Marker.HALF_SIZE, yOffset - Marker.HALF_SIZE, Marker.SIZE, Marker.SIZE); + double halfSize = (double) markerSize / 2; + Shape square = new Rectangle2D.Double(xOffset - halfSize, yOffset - halfSize, markerSize, markerSize); g.fill(square); } 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 19dc130c8d38d4089e3a716578da4dd2a9842882..6f53f66426131b076c320f2e85f0a01ddd6c3369 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 @@ -24,15 +24,16 @@ import java.awt.geom.Path2D; public class TriangleDown extends Marker { @Override - public void paint(Graphics2D g, double xOffset, double yOffset) { + public void paint(Graphics2D g, double xOffset, double yOffset, int markerSize) { g.setStroke(stroke); + double halfSize = (double) markerSize / 2; // Make a triangle Path2D.Double path = new Path2D.Double(); - path.moveTo(xOffset - Marker.HALF_SIZE, 1 + yOffset - Marker.HALF_SIZE); - path.lineTo(xOffset, 1 + yOffset - Marker.HALF_SIZE + Marker.SIZE + 1); - path.lineTo(xOffset - Marker.HALF_SIZE + Marker.SIZE + 1, 1 + yOffset - Marker.HALF_SIZE); + path.moveTo(xOffset - halfSize, 1 + yOffset - halfSize); + path.lineTo(xOffset, 1 + yOffset - halfSize + markerSize); + path.lineTo(xOffset - halfSize + markerSize, 1 + yOffset - halfSize); path.closePath(); g.fill(path); 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 7f9bf7087e15e26319b698e0c463b7dde1363318..3aa77c341fcb669831fadac6b024851114a19051 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 @@ -24,15 +24,16 @@ import java.awt.geom.Path2D; public class TriangleUp extends Marker { @Override - public void paint(Graphics2D g, double xOffset, double yOffset) { + public void paint(Graphics2D g, double xOffset, double yOffset, int markerSize) { g.setStroke(stroke); + double halfSize = (double) markerSize / 2; // Make a triangle Path2D.Double path = new Path2D.Double(); - path.moveTo(xOffset - Marker.HALF_SIZE, yOffset - Marker.HALF_SIZE + Marker.SIZE + 1); - path.lineTo(xOffset - Marker.HALF_SIZE + Marker.SIZE + 1, yOffset - Marker.HALF_SIZE + Marker.SIZE + 1); - path.lineTo(xOffset, yOffset - Marker.HALF_SIZE); + path.moveTo(xOffset - halfSize, yOffset - halfSize + markerSize - 1); + path.lineTo(xOffset - halfSize + markerSize, yOffset - halfSize + markerSize - 1); + path.lineTo(xOffset, yOffset - halfSize - 1); path.closePath(); g.fill(path); diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/package-info.java b/xchart/src/main/java/com/xeiam/xchart/internal/package-info.java index 809acc0e7af07726be7b8f8d1a39107ed4559bcb..1db80f3d3ea43ccb2268d21ea4865ff400090997 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/package-info.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/package-info.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /** * Classes in this package are internal and are not intended to be accessed directly. Therefore, they are not included in the JavaDocs. */ diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/style/GGPlot2Theme.java b/xchart/src/main/java/com/xeiam/xchart/internal/style/GGPlot2Theme.java index 1c99fe3c947592dff76fa03256eb6df37539dde2..3ccbfcbfca171db5abd305c92afb33152cfbba5f 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/style/GGPlot2Theme.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/style/GGPlot2Theme.java @@ -288,11 +288,19 @@ public class GGPlot2Theme implements Theme { } @Override - public boolean barsOverlapped() { + public boolean isBarsOverlapped() { return false; } + // Line, Scatter, Area Charts /////////////////////////////// + + @Override + public int getMarkerSize() { + + return 8; + } + // Error Bars /////////////////////////////// @Override diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/style/MatlabTheme.java b/xchart/src/main/java/com/xeiam/xchart/internal/style/MatlabTheme.java index 56bdc5f4d67ae4ea48c828cdbe0a9e739d01112f..d329e10e561c5f6353ff8a22017bef405adb2683 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/style/MatlabTheme.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/style/MatlabTheme.java @@ -289,11 +289,19 @@ public class MatlabTheme implements Theme { } @Override - public boolean barsOverlapped() { + public boolean isBarsOverlapped() { return false; } + // Line, Scatter, Area Charts /////////////////////////////// + + @Override + public int getMarkerSize() { + + return 8; + } + // Error Bars /////////////////////////////// @Override diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/style/Theme.java b/xchart/src/main/java/com/xeiam/xchart/internal/style/Theme.java index e4e37fe3f6885ad0e51e133215a7e3d2ae801cea..d67e64250ef3859ac47707a9c189809479227cf3 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/style/Theme.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/style/Theme.java @@ -120,7 +120,11 @@ public interface Theme { public double getBarWidthPercentage(); - public boolean barsOverlapped(); + public boolean isBarsOverlapped(); + + // Line, Scatter, Area Charts /////////////////////////////// + + public int getMarkerSize(); // Error Bars /////////////////////////////// diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/style/XChartTheme.java b/xchart/src/main/java/com/xeiam/xchart/internal/style/XChartTheme.java index 1afe96c09037759ef769451113e8749a8f54c637..bd292dd16924083a235c7aaa69df0b38c8b9b1bc 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/style/XChartTheme.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/style/XChartTheme.java @@ -288,11 +288,19 @@ public class XChartTheme implements Theme { } @Override - public boolean barsOverlapped() { + public boolean isBarsOverlapped() { return false; } + // Line, Scatter, Area Charts /////////////////////////////// + + @Override + public int getMarkerSize() { + + return 8; + } + // Error Bars /////////////////////////////// @Override diff --git a/xchart/src/test/java/com/xeiam/xchart/DateAxisTickCalculatorTest.java b/xchart/src/test/java/com/xeiam/xchart/DateAxisTickCalculatorTest.java deleted file mode 100644 index 051495ddeb63e2196ab2b5f512decb56da7df344..0000000000000000000000000000000000000000 --- a/xchart/src/test/java/com/xeiam/xchart/DateAxisTickCalculatorTest.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright 2011 - 2014 Xeiam LLC. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.xeiam.xchart; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; - -import java.util.Arrays; -import java.util.List; - -import org.junit.Test; - -import com.xeiam.xchart.internal.chartpart.Axis.Direction; -import com.xeiam.xchart.internal.chartpart.AxisTickDateCalculator; - -/** - * @author timmolter - */ -public class DateAxisTickCalculatorTest { - - @Test - public void testDateOneMinuteTimespan() { - - AxisTickDateCalculator decimalAxisTickCalculator = new AxisTickDateCalculator(Direction.X, 600, 1361110661000.0, 1361110721000.0, new StyleManager()); - - List<String> tickLabels = decimalAxisTickCalculator.getTickLabels(); - System.out.println(Arrays.toString(tickLabels.toArray())); - assertThat(tickLabels.size(), equalTo(6)); - assertThat(tickLabels.get(0), equalTo("17:50")); - - List<Double> tickLocations = decimalAxisTickCalculator.getTickLocations(); - System.out.println(Arrays.toString(tickLocations.toArray())); - assertThat(tickLocations.size(), equalTo(6)); - assertThat(tickLocations.get(0), equalTo(100.0)); - } -} diff --git a/xchart/src/test/java/com/xeiam/xchart/DecimalAxisTickCalculatorTest.java b/xchart/src/test/java/com/xeiam/xchart/DecimalAxisTickCalculatorTest.java deleted file mode 100644 index e616b9e7cd0ce81cc99e9c1de8948d45cd7448d9..0000000000000000000000000000000000000000 --- a/xchart/src/test/java/com/xeiam/xchart/DecimalAxisTickCalculatorTest.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright 2011 - 2014 Xeiam LLC. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.xeiam.xchart; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; - -import java.util.Arrays; -import java.util.List; - -import org.junit.Test; - -import com.xeiam.xchart.internal.chartpart.Axis.Direction; -import com.xeiam.xchart.internal.chartpart.AxisTickNumericalCalculator; - -/** - * @author timmolter - */ -public class DecimalAxisTickCalculatorTest { - - @Test - public void testDateOneMinuteTimespan() { - - AxisTickNumericalCalculator decimalAxisTickCalculator = new AxisTickNumericalCalculator(Direction.X, 600, -15, 15, new StyleManager()); - - List<String> tickLabels = decimalAxisTickCalculator.getTickLabels(); - System.out.println(Arrays.toString(tickLabels.toArray())); - assertThat(tickLabels.size(), equalTo(7)); - assertThat(tickLabels.get(0), equalTo("-15")); - - List<Double> tickLocations = decimalAxisTickCalculator.getTickLocations(); - System.out.println(Arrays.toString(tickLocations.toArray())); - assertThat(tickLocations.size(), equalTo(7)); - assertThat(tickLocations.get(0), equalTo(15.0)); - } -} diff --git a/xchart/src/test/java/com/xeiam/xchart/NumberFormatterTest.java b/xchart/src/test/java/com/xeiam/xchart/NumberFormatterTest.java deleted file mode 100644 index 91397cfd0dd32c0f790072988d52f3fd36e1c5a6..0000000000000000000000000000000000000000 --- a/xchart/src/test/java/com/xeiam/xchart/NumberFormatterTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright 2011 - 2014 Xeiam LLC. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.xeiam.xchart; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; - -import java.util.Locale; - -import org.junit.Test; - -import com.xeiam.xchart.internal.chartpart.NumberFormatter; - -/** - * @author timmolter - */ -public class NumberFormatterTest { - - private final Locale locale = Locale.US; - - @Test - public void testNumberFormatting() { - - StyleManager styleManager = new StyleManager(); - NumberFormatter numberFormatter = new NumberFormatter(styleManager); - - // big - styleManager.setLocale(locale); - - String stringValue = numberFormatter.formatNumber(1); - assertThat(stringValue, equalTo("1")); - - stringValue = numberFormatter.formatNumber(1000); - assertThat(stringValue, equalTo("1000")); - - stringValue = numberFormatter.formatNumber(9999); - assertThat(stringValue, equalTo("9999")); - - stringValue = numberFormatter.formatNumber(20000); - assertThat(stringValue, equalTo("2E4")); - - stringValue = numberFormatter.formatNumber(200.23); - assertThat(stringValue, equalTo("200.23")); - - // small - - stringValue = numberFormatter.formatNumber(0.01); - assertThat(stringValue, equalTo("0.01")); - - stringValue = numberFormatter.formatNumber(0.001); - assertThat(stringValue, equalTo("0.001")); - - stringValue = numberFormatter.formatNumber(0.0012); - assertThat(stringValue, equalTo("0.0012")); - - stringValue = numberFormatter.formatNumber(0.0001); - assertThat(stringValue, equalTo("1E-4")); - - stringValue = numberFormatter.formatNumber(.00012); - assertThat(stringValue, equalTo("1.2E-4")); - - stringValue = numberFormatter.formatNumber(0.0); - assertThat(stringValue, equalTo("0")); - - stringValue = numberFormatter.formatNumber(0); - assertThat(stringValue, equalTo("0")); - - // non-default - styleManager.setLocale(Locale.GERMANY); - - stringValue = numberFormatter.formatNumber(0.01); - assertThat(stringValue, equalTo("0,01")); - - stringValue = numberFormatter.formatNumber(200.23); - assertThat(stringValue, equalTo("200,23")); - - styleManager.setNormalDecimalPattern("#.#"); - stringValue = numberFormatter.formatNumber(200.23); - assertThat(stringValue, equalTo("200,2")); - - styleManager.setScientificDecimalPattern("0.#E0"); - stringValue = numberFormatter.formatNumber(2009764.23); - assertThat(stringValue, equalTo("2E6")); - - } - -}