From b3ecab7d3f0ff220acc5361dec397a8830fb30c6 Mon Sep 17 00:00:00 2001 From: Tim Molter <tim.molter@gmail.com> Date: Sat, 30 Jul 2011 17:07:04 +0200 Subject: [PATCH] release 1.0.2 --- build.properties | 2 +- sample/com/xeiam/examples/Example4.java | 2 +- sample/com/xeiam/examples/Example5.java | 45 ++++++++++++++++++++++++ sample/com/xeiam/examples/Example6.java | 44 +++++++++++++++++++++++ sample/com/xeiam/examples/Example7.java | 44 +++++++++++++++++++++++ src/com/xeiam/xcharts/Axis.java | 8 ++--- src/com/xeiam/xcharts/AxisPair.java | 43 ++++++++++++++++++++++ src/com/xeiam/xcharts/AxisTick.java | 40 +++++++++++---------- src/com/xeiam/xcharts/Chart.java | 5 +++ src/com/xeiam/xcharts/PlotContent.java | 18 +++++----- src/com/xeiam/xcharts/series/Series.java | 41 ++++++++++++++------- 11 files changed, 245 insertions(+), 47 deletions(-) create mode 100644 sample/com/xeiam/examples/Example5.java create mode 100644 sample/com/xeiam/examples/Example6.java create mode 100644 sample/com/xeiam/examples/Example7.java diff --git a/build.properties b/build.properties index 1439ef75..e2a43317 100644 --- a/build.properties +++ b/build.properties @@ -1,5 +1,5 @@ project.name=xchart -build.version=1.0.1 +build.version=1.0.2 lib.dir=lib src.dir=src web.dir=web diff --git a/sample/com/xeiam/examples/Example4.java b/sample/com/xeiam/examples/Example4.java index 9dd25ac6..2d4cf67a 100644 --- a/sample/com/xeiam/examples/Example4.java +++ b/sample/com/xeiam/examples/Example4.java @@ -51,7 +51,7 @@ public class Example4 { private static double[] getRandomWalk(int numPoints) { double[] y = new double[numPoints]; - y[0] = -1000; + y[0] = 0; for (int i = 1; i < y.length; i++) { y[i] = y[i - 1] + Math.random() - .5; } diff --git a/sample/com/xeiam/examples/Example5.java b/sample/com/xeiam/examples/Example5.java new file mode 100644 index 00000000..f0a9934a --- /dev/null +++ b/sample/com/xeiam/examples/Example5.java @@ -0,0 +1,45 @@ +/** + * Copyright 2011 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.examples; + +import com.xeiam.swing.SwingWrapper; +import com.xeiam.xcharts.Chart; + +/** + * Plot vertical and horizontal lines + * + * @author timmolter + */ +public class Example5 { + + public static void main(String[] args) { + + // Create Chart + Chart chart = new Chart(700, 500); + + // Customize Chart + chart.setChartTitle("Sample Chart"); + chart.setXAxisTitle("X"); + chart.setYAxisTitle("Y"); + + chart.addSeries("vertical", new double[] { 1, 1 }, new double[] { -10, 10 }); + chart.addSeries("horizontal", new double[] { -10, 10 }, new double[] { 0, 0 }); + + SwingWrapper swingHelper = new SwingWrapper(chart); + swingHelper.displayChart(); + } + +} diff --git a/sample/com/xeiam/examples/Example6.java b/sample/com/xeiam/examples/Example6.java new file mode 100644 index 00000000..f0b6c77d --- /dev/null +++ b/sample/com/xeiam/examples/Example6.java @@ -0,0 +1,44 @@ +/** + * Copyright 2011 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.examples; + +import com.xeiam.swing.SwingWrapper; +import com.xeiam.xcharts.Chart; + +/** + * Create chart with single point + * + * @author timmolter + */ +public class Example6 { + + public static void main(String[] args) { + + // Create Chart + Chart chart = new Chart(700, 500); + + // Customize Chart + chart.setChartTitle("Sample Chart"); + chart.setXAxisTitle("X"); + chart.setYAxisTitle("Y"); + + chart.addSeries("single point (1,1)", new double[] { 1 }, new double[] { 1 }); + + SwingWrapper swingHelper = new SwingWrapper(chart); + swingHelper.displayChart(); + } + +} diff --git a/sample/com/xeiam/examples/Example7.java b/sample/com/xeiam/examples/Example7.java new file mode 100644 index 00000000..c294d43d --- /dev/null +++ b/sample/com/xeiam/examples/Example7.java @@ -0,0 +1,44 @@ +/** + * Copyright 2011 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.examples; + +import com.xeiam.swing.SwingWrapper; +import com.xeiam.xcharts.Chart; + +/** + * Create chart with NaN values + * + * @author timmolter + */ +public class Example7 { + + public static void main(String[] args) { + + // Create Chart + Chart chart = new Chart(700, 500); + + // Customize Chart + chart.setChartTitle("Sample Chart"); + chart.setXAxisTitle("X"); + chart.setYAxisTitle("Y"); + + chart.addSeries("NaN Value at (2,2)", null, new double[] { 0, 1, Double.NaN, 3, 4 }); + + SwingWrapper swingHelper = new SwingWrapper(chart); + swingHelper.displayChart(); + } + +} diff --git a/src/com/xeiam/xcharts/Axis.java b/src/com/xeiam/xcharts/Axis.java index da921887..38888f25 100644 --- a/src/com/xeiam/xcharts/Axis.java +++ b/src/com/xeiam/xcharts/Axis.java @@ -47,9 +47,9 @@ public class Axis implements IChartPart { /** the axis direction */ private Direction direction; - private double min = Double.MAX_VALUE; + private Double min = null; - private double max = -Double.MAX_VALUE; + private Double max = null; /** the bounds */ private Rectangle bounds = new Rectangle(); // default all-zero rectangle @@ -93,10 +93,10 @@ public class Axis implements IChartPart { // System.out.println(min); // System.out.println(max); - if (min < this.min) { + if (this.min == null || min < this.min) { this.min = min; } - if (max > this.max) { + if (this.max == null || max > this.max) { this.max = max; } diff --git a/src/com/xeiam/xcharts/AxisPair.java b/src/com/xeiam/xcharts/AxisPair.java index 6fe117e3..c488cc11 100644 --- a/src/com/xeiam/xcharts/AxisPair.java +++ b/src/com/xeiam/xcharts/AxisPair.java @@ -58,16 +58,42 @@ public class AxisPair implements IChartPart { */ public Series addSeries(String seriesName, double[] xData, double[] yData) { + // Sanity checks + if (yData == null) { + throw new RuntimeException("Y-Axis data cannot be null!!!"); + } + if (yData.length == 0) { + throw new RuntimeException("Y-Axis data cannot be empty!!!"); + } + if (xData != null && xData.length == 0) { + throw new RuntimeException("X-Axis data cannot be empty!!!"); + } + if (xData != null && xData.length == 1 && Double.isNaN(yData[0])) { + throw new RuntimeException("X-Axis data cannot contain a single NaN value!!!"); + } + if (yData.length == 1 && Double.isNaN(yData[0])) { + throw new RuntimeException("Y-Axis data cannot contain a single NaN value!!!"); + } + Series series; if (xData != null) { + verifyValues(xData); + verifyValues(yData); series = new Series(seriesName, xData, yData); } else { // generate xData double[] generatedXData = new double[yData.length]; + verifyValues(yData); for (int i = 1; i < yData.length; i++) { generatedXData[i] = i; } series = new Series(seriesName, generatedXData, yData); } + + // Sanity check + if (xData != null && xData.length != yData.length) { + throw new RuntimeException("X and Y-Axis lengths are not the same!!! "); + } + seriesMap.put(seriesCount++, series); // add min/max to axis @@ -77,6 +103,22 @@ public class AxisPair implements IChartPart { return series; } + /** + * Checks for invalid values in data array + * + * @param data + */ + private void verifyValues(double[] data) { + + for (int i = 0; i < data.length; i++) { + if (data[i] == Double.POSITIVE_INFINITY) { + throw new RuntimeException("Axis data cannot contain Double.POSITIVE_INFINITY!!!"); + } else if (data[i] == Double.NEGATIVE_INFINITY) { + throw new RuntimeException("Axis data cannot contain Double.NEGATIVE_INFINITY!!!"); + } + } + } + protected Axis getXAxis() { return xAxis; } @@ -111,6 +153,7 @@ public class AxisPair implements IChartPart { @Override public void paint(Graphics2D g) { + yAxis.paint(g); xAxis.paint(g); } diff --git a/src/com/xeiam/xcharts/AxisTick.java b/src/com/xeiam/xcharts/AxisTick.java index d74d6176..0319132c 100644 --- a/src/com/xeiam/xcharts/AxisTick.java +++ b/src/com/xeiam/xcharts/AxisTick.java @@ -148,30 +148,32 @@ public class AxisTick implements IChartPart { int margin = AxisPair.getMargin(workingSpace, tickSpace); - final BigDecimal MIN = new BigDecimal(new Double(axis.getMin()).toString()); - BigDecimal firstPosition; - BigDecimal gridStep = getGridStep(tickSpace); - - double xyz = MIN.remainder(gridStep).doubleValue(); - if (xyz <= 0.0) { - firstPosition = MIN.subtract(MIN.remainder(gridStep)); + // a check if all axis data are the exact same values + if (axis.getMax() == axis.getMin()) { + tickLabels.add(format(axis.getMax())); + tickLocations.add((int) (margin + tickSpace / 2.0)); } else { - firstPosition = MIN.subtract(MIN.remainder(gridStep)).add(gridStep); - } - - for (BigDecimal b = firstPosition; b.doubleValue() <= axis.getMax(); b = b.add(gridStep)) { - // System.out.println("b= " + b); - tickLabels.add(format(b.doubleValue())); - int tickLabelPosition = (int) (margin + ((b.doubleValue() - axis.getMin()) / (axis.getMax() - axis.getMin()) * tickSpace)); - // System.out.println("tickLabelPosition= " + tickLabelPosition); + final BigDecimal MIN = new BigDecimal(new Double(axis.getMin()).toString()); + BigDecimal firstPosition; + BigDecimal gridStep = getGridStep(tickSpace); - // a check if all axis data are the exact same values - if (Math.abs(axis.getMax() - axis.getMin()) / 5 == 0.0) { - tickLabelPosition = (int) (margin + tickSpace / 2.0); + double xyz = MIN.remainder(gridStep).doubleValue(); + if (xyz <= 0.0) { + firstPosition = MIN.subtract(MIN.remainder(gridStep)); + } else { + firstPosition = MIN.subtract(MIN.remainder(gridStep)).add(gridStep); } - tickLocations.add(tickLabelPosition); + for (BigDecimal b = firstPosition; b.doubleValue() <= axis.getMax(); b = b.add(gridStep)) { + + // System.out.println("b= " + b); + tickLabels.add(format(b.doubleValue())); + int tickLabelPosition = (int) (margin + ((b.doubleValue() - axis.getMin()) / (axis.getMax() - axis.getMin()) * tickSpace)); + // System.out.println("tickLabelPosition= " + tickLabelPosition); + + tickLocations.add(tickLabelPosition); + } } } diff --git a/src/com/xeiam/xcharts/Chart.java b/src/com/xeiam/xcharts/Chart.java index f93caa87..1722e903 100644 --- a/src/com/xeiam/xcharts/Chart.java +++ b/src/com/xeiam/xcharts/Chart.java @@ -51,6 +51,11 @@ public class Chart { public void paint(final Graphics2D g) { + // Sanity check + if (axisPair.getSeriesMap().isEmpty()) { + throw new RuntimeException("No series defined for Chart!!!"); + } + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // global rendering hint g.setColor(ChartColor.getAWTColor(ChartColor.GREY)); g.fillRect(0, 0, width, height); diff --git a/src/com/xeiam/xcharts/PlotContent.java b/src/com/xeiam/xcharts/PlotContent.java index 6f645604..9446d921 100644 --- a/src/com/xeiam/xcharts/PlotContent.java +++ b/src/com/xeiam/xcharts/PlotContent.java @@ -72,15 +72,15 @@ public class PlotContent implements IChartPart { for (int i = 0; i < xData.length; i++) { - // if (Double.isInfinite(xData[i]) || Double.isNaN(xData[i])) { - // throw new RuntimeException("Infinite or NaN values in xAxis Data not allowed!!!"); - // } - // - // if (Double.isInfinite(yData[i]) || Double.isNaN(yData[i])) { - // throw new RuntimeException("Infinite or NaN values in yAxis Data not allowed!!!"); - // } - - if (!Double.isInfinite(xData[i]) && !Double.isNaN(xData[i]) && !Double.isInfinite(yData[i]) && !Double.isNaN(yData[i])) { + if (Double.isInfinite(xData[i])) { + throw new RuntimeException("Infinite values in xAxis Data not allowed!!!"); + } + + if (Double.isInfinite(yData[i])) { + throw new RuntimeException("Infinite values in yAxis Data not allowed!!!"); + } + + if (!Double.isNaN(xData[i]) && !Double.isNaN(yData[i])) { int xTransform = (int) (xLeftMargin + ((xData[i] - xMin) / (xMax - xMin) * xTickSpace)); int yTransform = (int) (bounds.getHeight() - (yTopMargin + (yData[i] - yMin) / (yMax - yMin) * yTickSpace)); diff --git a/src/com/xeiam/xcharts/series/Series.java b/src/com/xeiam/xcharts/series/Series.java index c15c5a74..4e1b47cf 100644 --- a/src/com/xeiam/xcharts/series/Series.java +++ b/src/com/xeiam/xcharts/series/Series.java @@ -17,7 +17,6 @@ package com.xeiam.xcharts.series; import java.awt.BasicStroke; import java.awt.Color; -import java.util.Arrays; import com.xeiam.xcharts.series.markers.Marker; @@ -33,16 +32,16 @@ public class Series { protected double[] yData; /** the minimum value of axis range */ - private double xMin = 0d; + private double xMin; /** the maximum value of axis range */ - private double xMax = 1d; + private double xMax; /** the minimum value of axis range */ - private double yMin = 0d; + private double yMin; /** the maximum value of axis range */ - private double yMax = 1d; + private double yMax; /** Line Style */ private BasicStroke stroke; @@ -70,16 +69,14 @@ public class Series { this.yData = yData; // xData - double[] xDataClone = xData.clone(); - Arrays.sort(xDataClone); - this.xMin = xDataClone[0]; - this.xMax = xDataClone[xDataClone.length - 1]; + double[] xMinMax = findMinMax(xData); + this.xMin = xMinMax[0]; + this.xMax = xMinMax[1]; // yData - double[] yDataClone = yData.clone(); - Arrays.sort(yDataClone); - this.yMin = yDataClone[0]; - this.yMax = yDataClone[yDataClone.length - 1]; + double[] yMinMax = findMinMax(yData); + this.yMin = yMinMax[0]; + this.yMax = yMinMax[1]; // System.out.println(yMin); // System.out.println(yMax); @@ -92,6 +89,24 @@ public class Series { } + private double[] findMinMax(double[] data) { + Double min = null; + Double max = null; + for (int i = 0; i < data.length; i++) { + if (min == null || data[i] < min) { + if (!Double.isNaN(data[i])) { + min = data[i]; + } + } + if (max == null || data[i] > max) { + if (!Double.isNaN(data[i])) { + max = data[i]; + } + } + } + return new double[] { min, max }; + } + public String getName() { return name; } -- GitLab