diff --git a/README.md b/README.md
index b356a1cf..ce7ea9ec 100644
--- a/README.md
+++ b/README.md
@@ -119,6 +119,7 @@ repaint.
* [x] Area charts
* [x] Step Area charts
* [x] Bar charts
+* [x] Horizontal bar charts
* [x] Histogram charts
* [x] Pie charts
* [x] Donut charts
@@ -151,17 +152,18 @@ repaint.
Currently, there are 5 major chart types. Each type has its corresponding `ChartBuilder`, `Styler` and `Series`.
-| Chart Type | Builder | Styler | Series | Allowed Data Types | Default Series Render Style |
-|---------------|----------------------|----------------|----------------|----------------------|-----------------------------|
-| XYChart | XYChartBuilder | XYStyler | XYSeries | Number, Date | Line |
-| CategoryChart | CategoryChartBuilder | CategoryStyler | CategorySeries | Number, Date, String | Bar |
-| PieChart | PieChartBuilder | PieStyler | PieSeries | String | Pie |
-| BubbleChart | BubbleChartBuilder | BubbleStyler | BubbleSeries | Number, Date | Round |
-| DialChart | DialChartBuilder | DialStyler | DialSeries | double | Round |
-| RadarChart | RadarChartBuilder | RadarStyler | RadarSeries | double[] | Round |
-| OHLCChart | OHLCChartBuilder | OHLCStyler | OHLCSeries | OHLC with Date | Candle |
-| BoxChart | BoxChartBuilder | BoxStyler | BoxSeries | Number, Date, String | Box |
-| HeatMapChart | HeatMapChartBuilder | HeatMapStyler | HeatMapSeries | Number, Date, String | -- |
+| Chart Type | Builder | Styler | Series | Allowed Data Types | Default Series Render Style |
+|--------------------|---------------------------|---------------------|---------------------|----------------------|-----------------------------|
+| XYChart | XYChartBuilder | XYStyler | XYSeries | Number, Date | Line |
+| CategoryChart | CategoryChartBuilder | CategoryStyler | CategorySeries | Number, Date, String | Bar |
+| PieChart | PieChartBuilder | PieStyler | PieSeries | String | Pie |
+| BubbleChart | BubbleChartBuilder | BubbleStyler | BubbleSeries | Number, Date | Round |
+| DialChart | DialChartBuilder | DialStyler | DialSeries | double | Round |
+| RadarChart | RadarChartBuilder | RadarStyler | RadarSeries | double[] | Round |
+| OHLCChart | OHLCChartBuilder | OHLCStyler | OHLCSeries | OHLC with Date | Candle |
+| BoxChart | BoxChartBuilder | BoxStyler | BoxSeries | Number, Date, String | Box |
+| HeatMapChart | HeatMapChartBuilder | HeatMapStyler | HeatMapSeries | Number, Date, String | -- |
+| HorizontalBarChart | HorizontalBarChartBuilder | HorizontalBarStyler | HorizontalBarSeries | Number, Date, String | Bar |
The different Stylers contain chart styling methods specific to the corresponding chart type as well as common styling methods common across all chart types.
@@ -284,6 +286,16 @@ An example of a set of sequence numbers: 12, 15, 17, 19, 20, 23, 25, 28, 30, 33,
`HeatMapChart` take Date, Number or String data types for the X-Axis, Y-Axis.
+### HorizontalBarChart
+
+![](https://raw.githubusercontent.com/knowm/XChart/develop/etc/XChart_HorizontalBarChart.png)
+
+`HorizontalBarChart` charts take Date, Number or String data types for the Y-Axis and Number data types for the X-Axis. For the Y-Axis, each category is given its own tick mark.
+
+It supports `labels` and `tooltips`, but more advanced features like `error bars` or `stacking` are not yet implemented.
+
+Series render style is `Bar`.
+
## Real-time Java Charts using XChart
![](https://raw.githubusercontent.com/knowm/XChart/develop/etc/XChart_SimpleRealtime.gif)
diff --git a/xchart-demo/src/main/java/org/knowm/xchart/demo/charts/horizontalbar/HorizontalBarChart01.java b/xchart-demo/src/main/java/org/knowm/xchart/demo/charts/horizontalbar/HorizontalBarChart01.java
new file mode 100644
index 00000000..a807a85b
--- /dev/null
+++ b/xchart-demo/src/main/java/org/knowm/xchart/demo/charts/horizontalbar/HorizontalBarChart01.java
@@ -0,0 +1,61 @@
+package org.knowm.xchart.demo.charts.horizontalbar;
+
+import org.knowm.xchart.HorizontalBarChart;
+import org.knowm.xchart.HorizontalBarChartBuilder;
+import org.knowm.xchart.SwingWrapper;
+import org.knowm.xchart.demo.charts.ExampleChart;
+import org.knowm.xchart.style.Styler.LegendPosition;
+
+import java.util.Arrays;
+
+/**
+ * Basic Horizontal Bar Chart
+ *
+ *
Demonstrates the following:
+ *
+ *
+ * - Integer categories as List
+ *
- All positive values
+ *
- Single series
+ *
- Place legend at Inside-NW position
+ *
- Bar Chart Annotations
+ */
+public class HorizontalBarChart01 implements ExampleChart {
+
+ public static void main(String[] args) {
+
+ ExampleChart exampleChart = new HorizontalBarChart01();
+ HorizontalBarChart chart = exampleChart.getChart();
+ new SwingWrapper<>(chart).displayChart();
+ }
+
+ @Override
+ public HorizontalBarChart getChart() {
+
+ // Create Chart
+ HorizontalBarChart chart =
+ new HorizontalBarChartBuilder()
+ .width(800)
+ .height(600)
+ .title(getClass().getSimpleName())
+ .yAxisTitle("Score")
+ .xAxisTitle("Number")
+ .build();
+
+ // Customize Chart
+ chart.getStyler().setLegendPosition(LegendPosition.InsideNW);
+ chart.getStyler().setLabelsVisible(false);
+ chart.getStyler().setPlotGridLinesVisible(false);
+
+ // Series
+ chart.addSeries("test 1", Arrays.asList(4, 5, 9, 6, 5), Arrays.asList(0, 1, 2, 3, 4));
+
+ return chart;
+ }
+
+ @Override
+ public String getExampleChartName() {
+
+ return getClass().getSimpleName() + " - Basic Horizontal Bar Chart";
+ }
+}
diff --git a/xchart-demo/src/main/java/org/knowm/xchart/demo/charts/horizontalbar/HorizontalBarChart02.java b/xchart-demo/src/main/java/org/knowm/xchart/demo/charts/horizontalbar/HorizontalBarChart02.java
new file mode 100644
index 00000000..454199e4
--- /dev/null
+++ b/xchart-demo/src/main/java/org/knowm/xchart/demo/charts/horizontalbar/HorizontalBarChart02.java
@@ -0,0 +1,85 @@
+package org.knowm.xchart.demo.charts.horizontalbar;
+
+import org.knowm.xchart.HorizontalBarChart;
+import org.knowm.xchart.HorizontalBarChartBuilder;
+import org.knowm.xchart.HorizontalBarSeries;
+import org.knowm.xchart.SwingWrapper;
+import org.knowm.xchart.demo.charts.ExampleChart;
+import org.knowm.xchart.style.Styler.ChartTheme;
+
+import java.awt.*;
+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;
+
+/**
+ * Date Categories
+ *
+ *
Demonstrates the following:
+ *
+ *
+ * - Date categories as List
+ *
- All negative values
+ *
- Single series
+ *
- No horizontal plot gridlines
+ *
- Change series color
+ *
- MATLAB Theme
+ */
+public class HorizontalBarChart02 implements ExampleChart {
+
+ public static void main(String[] args) {
+
+ ExampleChart exampleChart = new HorizontalBarChart02();
+ HorizontalBarChart chart = exampleChart.getChart();
+ new SwingWrapper<>(chart).displayChart();
+ }
+
+ @Override
+ public HorizontalBarChart getChart() {
+
+ // Create Chart
+ HorizontalBarChart chart =
+ new HorizontalBarChartBuilder()
+ .theme(ChartTheme.Matlab)
+ .width(800)
+ .height(600)
+ .title(getClass().getSimpleName())
+ .yAxisTitle("Year")
+ .xAxisTitle("Units Sold")
+ .build();
+
+ // Customize Chart
+ chart.getStyler().setPlotGridLinesVisible(false);
+ chart.getStyler().setDatePattern("yyyy");
+
+ // Series
+ List yData = new ArrayList();
+ List xData = new ArrayList();
+
+ Random random = new Random();
+ DateFormat sdf = new SimpleDateFormat("yyyy");
+ Date date = null;
+ for (int i = 1; i <= 8; i++) {
+ try {
+ date = sdf.parse("" + (2000 + i));
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ yData.add(date);
+ xData.add(-1 * 0.00000001 * ((random.nextInt(i) + 1)));
+ }
+ HorizontalBarSeries series = chart.addSeries("Model 77", xData, yData);
+ series.setFillColor(new Color(230, 150, 150));
+
+ return chart;
+ }
+
+ @Override
+ public String getExampleChartName() {
+ return getClass().getSimpleName() + " - Date Categories";
+ }
+}
diff --git a/xchart-demo/src/main/java/org/knowm/xchart/demo/charts/horizontalbar/HorizontalBarChart03.java b/xchart-demo/src/main/java/org/knowm/xchart/demo/charts/horizontalbar/HorizontalBarChart03.java
new file mode 100644
index 00000000..b9095936
--- /dev/null
+++ b/xchart-demo/src/main/java/org/knowm/xchart/demo/charts/horizontalbar/HorizontalBarChart03.java
@@ -0,0 +1,78 @@
+package org.knowm.xchart.demo.charts.horizontalbar;
+
+import org.knowm.xchart.HorizontalBarChart;
+import org.knowm.xchart.HorizontalBarChartBuilder;
+import org.knowm.xchart.SwingWrapper;
+import org.knowm.xchart.demo.charts.ExampleChart;
+import org.knowm.xchart.style.Styler.ChartTheme;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * GGPlot2 Theme Horizontal Bar chart
+ *
+ *
Demonstrates the following:
+ *
+ *
+ * - String categories
+ *
- Positive and negative values
+ *
- Multiple series
+ */
+public class HorizontalBarChart03 implements ExampleChart {
+
+ public static void main(String[] args) {
+
+ ExampleChart exampleChart = new HorizontalBarChart03();
+ HorizontalBarChart chart = exampleChart.getChart();
+ new SwingWrapper<>(chart).displayChart();
+ }
+
+ @Override
+ public HorizontalBarChart getChart() {
+
+ // Create Chart
+ HorizontalBarChart chart =
+ new HorizontalBarChartBuilder()
+ .width(800)
+ .height(600)
+ .title(getClass().getSimpleName())
+ .yAxisTitle("Color")
+ .xAxisTitle("Temperature")
+ .theme(ChartTheme.GGPlot2)
+ .build();
+
+ // Customize Chart
+ chart.getStyler().setPlotGridVerticalLinesVisible(false);
+
+ // Series
+ chart.addSeries(
+ "fish",
+ new ArrayList(Arrays.asList(new Number[]{-40, 30, 20, 60, 60})),
+ new ArrayList<>(Arrays.asList(new String[]{"Blue", "Red", "Green", "Yellow", "Orange"})));
+ chart.addSeries(
+ "worms",
+ new ArrayList(Arrays.asList(new Number[]{50, 10, -20, 40, 60})),
+ new ArrayList<>(Arrays.asList(new String[]{"Blue", "Red", "Green", "Yellow", "Orange"})));
+ chart.addSeries(
+ "birds",
+ new ArrayList(Arrays.asList(new Number[]{13, 22, -23, -34, 37})),
+ new ArrayList<>(Arrays.asList(new String[]{"Blue", "Red", "Green", "Yellow", "Orange"})));
+ chart.addSeries(
+ "ants",
+ new ArrayList(Arrays.asList(new Number[]{50, 57, -14, -20, 31})),
+ new ArrayList<>(Arrays.asList(new String[]{"Blue", "Red", "Green", "Yellow", "Orange"})));
+ chart.addSeries(
+ "slugs",
+ new ArrayList(Arrays.asList(new Number[]{-2, 29, 49, -16, -43})),
+ new ArrayList<>(Arrays.asList(new String[]{"Blue", "Red", "Green", "Yellow", "Orange"})));
+
+ return chart;
+ }
+
+ @Override
+ public String getExampleChartName() {
+
+ return getClass().getSimpleName() + " - GGPlot2 Theme";
+ }
+}
diff --git a/xchart-demo/src/main/java/org/knowm/xchart/demo/charts/horizontalbar/HorizontalBarChart04.java b/xchart-demo/src/main/java/org/knowm/xchart/demo/charts/horizontalbar/HorizontalBarChart04.java
new file mode 100644
index 00000000..33ee4bd4
--- /dev/null
+++ b/xchart-demo/src/main/java/org/knowm/xchart/demo/charts/horizontalbar/HorizontalBarChart04.java
@@ -0,0 +1,66 @@
+package org.knowm.xchart.demo.charts.horizontalbar;
+
+import org.knowm.xchart.HorizontalBarChart;
+import org.knowm.xchart.HorizontalBarChartBuilder;
+import org.knowm.xchart.SwingWrapper;
+import org.knowm.xchart.demo.charts.ExampleChart;
+import org.knowm.xchart.style.Styler;
+
+import java.util.Arrays;
+
+/**
+ * Multiple series, labels and tooltips
+ *
+ *
Demonstrates the following:
+ *
+ *
+ * - Number categories
+ *
- Positive values
+ *
- Multiple series
+ *
- Missing point in series
+ *
- Labels
+ *
- Bar Chart Annotations
+ *
- Horizontal Legend OutsideS
+ */
+public class HorizontalBarChart04 implements ExampleChart {
+
+ public static void main(String[] args) {
+
+ ExampleChart exampleChart = new HorizontalBarChart04();
+ HorizontalBarChart chart = exampleChart.getChart();
+ new SwingWrapper<>(chart).displayChart();
+ }
+
+ @Override
+ public HorizontalBarChart getChart() {
+
+ // Create Chart
+ HorizontalBarChart chart =
+ new HorizontalBarChartBuilder()
+ .width(800)
+ .height(600)
+ .title(getClass().getSimpleName())
+ .yAxisTitle("Age")
+ .xAxisTitle("XFactor")
+ .build();
+
+ // Customize Chart
+ chart.getStyler().setLabelsVisible(true);
+ chart.getStyler().setToolTipsEnabled(true);
+ chart.getStyler().setPlotGridVerticalLinesVisible(false);
+ chart.getStyler().setLegendPosition(Styler.LegendPosition.OutsideS);
+ chart.getStyler().setLegendLayout(Styler.LegendLayout.Horizontal);
+
+ // Series
+ chart.addSeries("female", Arrays.asList(50, 10, 20, 40, 35), Arrays.asList(10, 20, 30, 40, 50));
+ chart.addSeries("male", Arrays.asList(40, 30, 20, null, 60), Arrays.asList(10, 20, 30, 40, 50));
+
+ return chart;
+ }
+
+ @Override
+ public String getExampleChartName() {
+
+ return getClass().getSimpleName() + " - Multiple series, labels and tooltips";
+ }
+}
diff --git a/xchart/src/main/java/org/knowm/xchart/HorizontalBarChart.java b/xchart/src/main/java/org/knowm/xchart/HorizontalBarChart.java
new file mode 100644
index 00000000..b9b9402b
--- /dev/null
+++ b/xchart/src/main/java/org/knowm/xchart/HorizontalBarChart.java
@@ -0,0 +1,247 @@
+package org.knowm.xchart;
+
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.knowm.xchart.internal.Utils;
+import org.knowm.xchart.internal.chartpart.AxisPair;
+import org.knowm.xchart.internal.chartpart.Chart;
+import org.knowm.xchart.internal.chartpart.Legend_HorizontalBar;
+import org.knowm.xchart.internal.chartpart.Plot_HorizontalBar;
+import org.knowm.xchart.internal.style.SeriesColorMarkerLineStyle;
+import org.knowm.xchart.internal.style.SeriesColorMarkerLineStyleCycler;
+import org.knowm.xchart.style.HorizontalBarStyler;
+import org.knowm.xchart.style.Styler.ChartTheme;
+import org.knowm.xchart.style.theme.Theme;
+
+public class HorizontalBarChart extends Chart {
+
+ /**
+ * Constructor - the default Chart Theme will be used (XChartTheme)
+ *
+ * @param width
+ * @param height
+ */
+ public HorizontalBarChart(int width, int height) {
+
+ super(width, height, new HorizontalBarStyler());
+ axisPair = new AxisPair(this);
+ plot = new Plot_HorizontalBar(this);
+ legend = new Legend_HorizontalBar(this);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param width
+ * @param height
+ * @param theme - pass in a instance of Theme class, probably a custom Theme.
+ */
+ public HorizontalBarChart(int width, int height, Theme theme) {
+
+ this(width, height);
+ styler.setTheme(theme);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param width
+ * @param height
+ * @param chartTheme - pass in the desired ChartTheme enum
+ */
+ public HorizontalBarChart(int width, int height, ChartTheme chartTheme) {
+
+ this(width, height, chartTheme.newInstance(chartTheme));
+ }
+
+ /**
+ * Constructor
+ *
+ * @param chartBuilder
+ */
+ public HorizontalBarChart(HorizontalBarChartBuilder chartBuilder) {
+
+ this(chartBuilder.width, chartBuilder.height, chartBuilder.chartTheme);
+ setTitle(chartBuilder.title);
+ setXAxisTitle(chartBuilder.xAxisTitle);
+ setYAxisTitle(chartBuilder.yAxisTitle);
+ }
+
+ /**
+ * Add a series for a horizontal bar type chart using double arrays with error bars
+ *
+ * @param seriesName
+ * @param xData the X-Axis data
+ * @param yData the Y-Axis data
+ * @return A Series object that you can set properties on
+ */
+ public HorizontalBarSeries addSeries(String seriesName, double[] xData, double[] yData) {
+
+ return addSeries(
+ seriesName,
+ Utils.getNumberListFromDoubleArray(xData),
+ Utils.getNumberListFromDoubleArray(yData));
+ }
+
+ /**
+ * Add a series for a horizontal bar type chart using int arrays with error bars
+ *
+ * @param seriesName
+ * @param xData the X-Axis data
+ * @param yData the Y-Axis data
+ * @return A Series object that you can set properties on
+ */
+ public HorizontalBarSeries addSeries(String seriesName, int[] xData, int[] yData) {
+
+ return addSeries(
+ seriesName, Utils.getNumberListFromIntArray(xData), Utils.getNumberListFromIntArray(yData));
+ }
+
+ /**
+ * Add a series for a horizontal bar type chart using Lists
+ *
+ * @param seriesName
+ * @param xData the X-Axis data
+ * @param yData the Y-Axis data
+ * @return A Series object that you can set properties on
+ */
+ public HorizontalBarSeries addSeries(
+ String seriesName, List extends Number> xData, List> yData) {
+
+ // Sanity checks
+ sanityCheck(seriesName, xData, yData);
+
+ HorizontalBarSeries series;
+ if (xData != null) {
+
+ // Sanity check
+ if (xData.size() != yData.size()) {
+ throw new IllegalArgumentException("X and Y-Axis sizes are not the same!!!");
+ }
+
+ } else { // generate xData
+ xData = Utils.getGeneratedDataAsList(yData.size());
+ }
+ series = new HorizontalBarSeries(seriesName, xData, yData);
+
+ seriesMap.put(seriesName, series);
+
+ return series;
+ }
+
+ /**
+ * Update a series by updating the X-Axis and Y-Axis
+ *
+ * @param seriesName
+ * @param newXData
+ * @param newYData - set null to be automatically generated as a list of increasing Integers
+ * starting from 1 and ending at the size of the new X-Axis data list.
+ * @return
+ */
+ public HorizontalBarSeries updateCategorySeries(
+ String seriesName, List extends Number> newXData, List> newYData) {
+
+ Map seriesMap = getSeriesMap();
+ HorizontalBarSeries series = seriesMap.get(seriesName);
+ if (series == null) {
+ throw new IllegalArgumentException("Series name >" + seriesName + "< not found!!!");
+ }
+ if (newYData == null) {
+ // generate Y-Data
+ List generatedYData = new ArrayList();
+ for (int i = 1; i <= newXData.size(); i++) {
+ generatedYData.add(i);
+ }
+ series.replaceData(newXData, generatedYData);
+ } else {
+ series.replaceData(newXData, newYData);
+ }
+
+ return series;
+ }
+
+ /**
+ * Update a series by updating the X-Axis, Y-Axis and error bar data
+ *
+ * @param seriesName
+ * @param newXData
+ * @param newYData - set null to be automatically generated as a list of increasing Integers
+ * starting from 1 and ending at the size of the new X-Axis data list.
+ * @return
+ */
+ public HorizontalBarSeries updateCategorySeries(
+ String seriesName, double[] newXData, double[] newYData) {
+
+ return updateCategorySeries(
+ seriesName,
+ Utils.getNumberListFromDoubleArray(newXData),
+ Utils.getNumberListFromDoubleArray(newYData));
+ }
+
+ ///////////////////////////////////////////////////
+ // Internal Members and Methods ///////////////////
+ ///////////////////////////////////////////////////
+
+ private void sanityCheck(String seriesName, List extends Number> xData, List> yData) {
+
+ if (seriesMap.containsKey(seriesName)) {
+ throw new IllegalArgumentException(
+ "Series name >"
+ + seriesName
+ + "< has already been used. Use unique names for each series!!!");
+ }
+ if (yData == null) {
+ throw new IllegalArgumentException("Y-Axis data cannot be null!!!");
+ }
+ if (yData.size() == 0) {
+ throw new IllegalArgumentException("Y-Axis data cannot be empty!!!");
+ }
+ if (xData != null && xData.size() == 0) {
+ throw new IllegalArgumentException("X-Axis data cannot be empty!!!");
+ }
+ }
+
+ @Override
+ public void paint(Graphics2D g, int width, int height) {
+
+ setWidth(width);
+ setHeight(height);
+
+ setSeriesStyles();
+
+ paintBackground(g);
+
+ axisPair.paint(g);
+ plot.paint(g);
+ chartTitle.paint(g);
+ legend.paint(g);
+ annotations.forEach(x -> x.paint(g));
+ }
+
+ /** set the series color, marker and line style based on theme */
+ private void setSeriesStyles() {
+
+ SeriesColorMarkerLineStyleCycler seriesColorMarkerLineStyleCycler =
+ new SeriesColorMarkerLineStyleCycler(
+ getStyler().getSeriesColors(),
+ getStyler().getSeriesMarkers(),
+ getStyler().getSeriesLines());
+ for (HorizontalBarSeries series : getSeriesMap().values()) {
+
+ SeriesColorMarkerLineStyle seriesColorMarkerLineStyle =
+ seriesColorMarkerLineStyleCycler.getNextSeriesColorMarkerLineStyle();
+
+ if (series.getLineStyle() == null) { // wasn't set manually
+ series.setLineStyle(seriesColorMarkerLineStyle.getStroke());
+ }
+ if (series.getLineColor() == null) { // wasn't set manually
+ series.setLineColor(seriesColorMarkerLineStyle.getColor());
+ }
+ if (series.getFillColor() == null) { // wasn't set manually
+ series.setFillColor(seriesColorMarkerLineStyle.getColor());
+ }
+ }
+ }
+}
diff --git a/xchart/src/main/java/org/knowm/xchart/HorizontalBarChartBuilder.java b/xchart/src/main/java/org/knowm/xchart/HorizontalBarChartBuilder.java
new file mode 100644
index 00000000..1c8cfa73
--- /dev/null
+++ b/xchart/src/main/java/org/knowm/xchart/HorizontalBarChartBuilder.java
@@ -0,0 +1,35 @@
+package org.knowm.xchart;
+
+import org.knowm.xchart.internal.ChartBuilder;
+
+public class HorizontalBarChartBuilder
+ extends ChartBuilder {
+
+ String xAxisTitle = "";
+ String yAxisTitle = "";
+
+ public HorizontalBarChartBuilder() {}
+
+ public HorizontalBarChartBuilder xAxisTitle(String xAxisTitle) {
+
+ this.xAxisTitle = xAxisTitle;
+ return this;
+ }
+
+ public HorizontalBarChartBuilder yAxisTitle(String yAxisTitle) {
+
+ this.yAxisTitle = yAxisTitle;
+ return this;
+ }
+
+ /**
+ * return fully built Chart_Category
+ *
+ * @return a CategoryChart
+ */
+ @Override
+ public HorizontalBarChart build() {
+
+ return new HorizontalBarChart(this);
+ }
+}
diff --git a/xchart/src/main/java/org/knowm/xchart/HorizontalBarSeries.java b/xchart/src/main/java/org/knowm/xchart/HorizontalBarSeries.java
new file mode 100644
index 00000000..0acca6b3
--- /dev/null
+++ b/xchart/src/main/java/org/knowm/xchart/HorizontalBarSeries.java
@@ -0,0 +1,121 @@
+package org.knowm.xchart;
+
+import java.util.*;
+import org.knowm.xchart.internal.chartpart.RenderableSeries.LegendRenderType;
+import org.knowm.xchart.internal.series.AxesChartSeries;
+
+/** A Series containing horizontal bar data to be plotted on a Chart */
+public class HorizontalBarSeries extends AxesChartSeries {
+
+ List extends Number> xData;
+
+ List> yData;
+
+ /**
+ * Constructor
+ *
+ * @param name
+ * @param xData
+ * @param yData
+ */
+ public HorizontalBarSeries(String name, List extends Number> xData, List> yData) {
+
+ super(name, getDataType(xData), getDataType(yData));
+ this.xData = xData;
+ this.yData = yData;
+ calculateMinMax();
+ }
+
+ public void replaceData(List extends Number> xData, List> yData) {
+
+ this.xData = xData;
+ this.yData = yData;
+ calculateMinMax();
+ }
+
+ @Override
+ protected void calculateMinMax() {
+
+ // xData
+ double[] xMinMax = findMinMax(xData, getxAxisDataType());
+ xMin = xMinMax[0];
+ xMax = xMinMax[1];
+ // System.out.println(xMin);
+ // System.out.println(xMax);
+
+ // yData
+ double[] yMinMax;
+ yMinMax = findMinMax(yData, getyAxisDataType());
+ yMin = yMinMax[0];
+ yMax = yMinMax[1];
+ // System.out.println(yMin);
+ // System.out.println(yMax);
+ }
+
+ double[] findMinMax(Collection> data, DataType dataType) {
+
+ double min = Double.MAX_VALUE;
+ double max = -Double.MAX_VALUE;
+
+ for (Object dataPoint : data) {
+
+ if (dataPoint == null) {
+ continue;
+ }
+
+ double value = 0.0;
+
+ if (dataType == DataType.Number) {
+ value = ((Number) dataPoint).doubleValue();
+ } else if (dataType == DataType.Date) {
+ Date date = (Date) dataPoint;
+ value = date.getTime();
+ } else if (dataType == DataType.String) {
+ return new double[] {Double.NaN, Double.NaN};
+ }
+ if (value < min) {
+ min = value;
+ }
+ if (value > max) {
+ max = value;
+ }
+ }
+
+ return new double[] {min, max};
+ }
+
+ @Override
+ public LegendRenderType getLegendRenderType() {
+
+ return null;
+ }
+
+ private static DataType getDataType(List> data) {
+
+ DataType axisType;
+
+ Iterator> itr = data.iterator();
+ Object dataPoint = itr.next();
+ if (dataPoint instanceof Number) {
+ axisType = DataType.Number;
+ } else if (dataPoint instanceof Date) {
+ axisType = DataType.Date;
+ } else if (dataPoint instanceof String) {
+ axisType = DataType.String;
+ } else {
+ throw new IllegalArgumentException(
+ "Series data must be either Number, Date or String type!!!");
+ }
+ return axisType;
+ }
+
+ public List extends Number> getXData() {
+
+ return xData;
+ }
+
+ public List> getYData() {
+
+ return yData;
+ }
+}
diff --git a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Axis.java b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Axis.java
index 8a08d2fc..7083cc50 100644
--- a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Axis.java
+++ b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Axis.java
@@ -7,30 +7,18 @@
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
+import java.util.*;
import java.util.stream.Collectors;
-import org.knowm.xchart.CategoryChart;
-import org.knowm.xchart.CategorySeries;
-import org.knowm.xchart.HeatMapChart;
-import org.knowm.xchart.XYChart;
-import org.knowm.xchart.XYSeries;
+import org.knowm.xchart.*;
+import org.knowm.xchart.HorizontalBarSeries;
import org.knowm.xchart.internal.Utils;
import org.knowm.xchart.internal.series.AxesChartSeries;
import org.knowm.xchart.internal.series.AxesChartSeriesCategory;
import org.knowm.xchart.internal.series.Series;
import org.knowm.xchart.internal.series.Series.DataType;
-import org.knowm.xchart.style.AxesChartStyler;
-import org.knowm.xchart.style.BoxStyler;
-import org.knowm.xchart.style.CategoryStyler;
-import org.knowm.xchart.style.HeatMapStyler;
+import org.knowm.xchart.style.*;
import org.knowm.xchart.style.Styler.LegendPosition;
import org.knowm.xchart.style.Styler.YAxisPosition;
-import org.knowm.xchart.style.XYStyler;
/** Axis */
public class Axis implements ChartPart {
@@ -395,7 +383,21 @@ private AxisTickCalculator getAxisTickCalculator(double workingSpace) {
private AxisTickCalculator getAxisTickCalculatorForY(double workingSpace) {
List yData = new ArrayList<>();
- if (axesChartStyler instanceof HeatMapStyler) {
+ if (axesChartStyler instanceof HorizontalBarStyler) {
+ Set uniqueYData = new LinkedHashSet<>();
+ for (HorizontalBarSeries categorySeries :
+ ((HorizontalBarChart) chart).getSeriesMap().values()) {
+ uniqueYData.addAll(
+ categorySeries.getYData().stream()
+ .filter(Objects::nonNull)
+ .filter(it -> it instanceof Number)
+ .mapToDouble(it -> ((Number) it).doubleValue())
+ .boxed()
+ .collect(Collectors.toList()));
+ }
+ yData.addAll(uniqueYData);
+ Collections.reverse(yData);
+ } else if (axesChartStyler instanceof HeatMapStyler) {
List> categories = ((HeatMapChart) chart).getHeatMapSeries().getYData();
yData =
categories.stream()
@@ -446,6 +448,18 @@ private AxisTickCalculator getAxisTickCalculatorForY(double workingSpace) {
return new AxisTickCalculator_Logarithmic(
getDirection(), workingSpace, min, max, axesChartStyler, getYIndex());
+ } else if (axesChartStyler instanceof HorizontalBarStyler) {
+ List> categories =
+ ((HorizontalBarChart) chart)
+ .getSeriesMap().values().stream()
+ .flatMap(it -> it.getYData().stream())
+ .distinct()
+ .collect(Collectors.toCollection(ArrayList::new));
+ Collections.reverse(categories);
+ DataType axisType = chart.getAxisPair().getYAxis().getDataType();
+
+ return new AxisTickCalculator_Category(
+ getDirection(), workingSpace, categories, axisType, axesChartStyler);
} else if (axesChartStyler instanceof HeatMapStyler) {
List> categories = ((HeatMapChart) chart).getHeatMapSeries().getYData();
@@ -465,7 +479,20 @@ private AxisTickCalculator getAxisTickCalculatorForY(double workingSpace) {
private AxisTickCalculator_ getAxisTickCalculatorForX(double workingSpace) {
List xData = new ArrayList<>();
- if (axesChartStyler instanceof HeatMapStyler) {
+ if (axesChartStyler instanceof HorizontalBarStyler) {
+ Set uniqueXData = new LinkedHashSet<>();
+ for (HorizontalBarSeries categorySeries :
+ ((HorizontalBarChart) chart).getSeriesMap().values()) {
+ List numericCategoryXData =
+ categorySeries.getXData().stream()
+ .filter(Objects::nonNull)
+ .mapToDouble(Number::doubleValue)
+ .boxed()
+ .collect(Collectors.toList());
+ uniqueXData.addAll(numericCategoryXData);
+ }
+ xData.addAll(uniqueXData);
+ } else if (axesChartStyler instanceof HeatMapStyler) {
List> categories = ((HeatMapChart) chart).getHeatMapSeries().getXData();
xData =
categories.stream()
@@ -514,6 +541,9 @@ private AxisTickCalculator_ getAxisTickCalculatorForX(double workingSpace) {
max,
axesChartStyler);
+ } else if (axesChartStyler instanceof HorizontalBarStyler) {
+ return new AxisTickCalculator_Number(
+ getDirection(), workingSpace, min, max, xData, axesChartStyler);
} else if (axesChartStyler instanceof CategoryStyler || axesChartStyler instanceof BoxStyler) {
// TODO Cleanup? More elegant way?
diff --git a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/AxisPair.java b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/AxisPair.java
index 4f9bd3ec..6a01af28 100644
--- a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/AxisPair.java
+++ b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/AxisPair.java
@@ -12,6 +12,7 @@
import org.knowm.xchart.style.AxesChartStyler;
import org.knowm.xchart.style.BoxStyler;
import org.knowm.xchart.style.CategoryStyler;
+import org.knowm.xchart.style.HorizontalBarStyler;
import org.knowm.xchart.style.Styler.LegendPosition;
import org.knowm.xchart.style.Styler.YAxisPosition;
@@ -375,6 +376,16 @@ private void overrideMinMaxForXAxis() {
double overrideXAxisMinValue = xAxis.getMin();
double overrideXAxisMaxValue = xAxis.getMax();
+
+ if (chart.getStyler() instanceof HorizontalBarStyler) {
+ if (xAxis.getMin() > 0.0) {
+ overrideXAxisMinValue = 0.0;
+ }
+ if (xAxis.getMax() < 0.0) {
+ overrideXAxisMaxValue = 0.0;
+ }
+ }
+
// override min and maxValue if specified
if (chart.getStyler().getXAxisMin() != null) {
diff --git a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Legend_HorizontalBar.java b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Legend_HorizontalBar.java
new file mode 100644
index 00000000..697af5b4
--- /dev/null
+++ b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Legend_HorizontalBar.java
@@ -0,0 +1,79 @@
+package org.knowm.xchart.internal.chartpart;
+
+import java.awt.*;
+import java.awt.geom.Rectangle2D;
+import java.util.Map;
+import org.knowm.xchart.HorizontalBarSeries;
+import org.knowm.xchart.internal.chartpart.RenderableSeries.LegendRenderType;
+import org.knowm.xchart.style.Styler;
+
+public class Legend_HorizontalBar
+ extends Legend_ {
+
+ private final ST axesChartStyler;
+
+ /**
+ * Constructor
+ *
+ * @param chart
+ */
+ public Legend_HorizontalBar(Chart chart) {
+
+ super(chart);
+ axesChartStyler = chart.getStyler();
+ }
+
+ @Override
+ public void doPaint(Graphics2D g) {
+
+ // Draw legend content inside legend box
+ double startx = xOffset + chart.getStyler().getLegendPadding();
+ double starty = yOffset + chart.getStyler().getLegendPadding();
+
+ Object oldHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+
+ Map map = chart.getSeriesMap();
+ for (S series : map.values()) {
+
+ if (!series.isShowInLegend()) {
+ continue;
+ }
+ if (!series.isEnabled()) {
+ continue;
+ }
+
+ Map seriesTextBounds = getSeriesTextBounds(series);
+ float legendEntryHeight = getLegendEntryHeight(seriesTextBounds, BOX_SIZE);
+
+ // paint line and marker
+
+ // paint inner box
+ Shape rectSmall = new Rectangle2D.Double(startx, starty, BOX_SIZE, BOX_SIZE);
+ g.setColor(series.getFillColor());
+ g.fill(rectSmall);
+
+ // paint series text
+ double x = startx + BOX_SIZE + chart.getStyler().getLegendPadding();
+ paintSeriesText(g, seriesTextBounds, BOX_SIZE, x, starty);
+
+ if (chart.getStyler().getLegendLayout() == Styler.LegendLayout.Vertical) {
+ starty += legendEntryHeight + chart.getStyler().getLegendPadding();
+ } else {
+ int markerWidth = BOX_SIZE;
+ if (series.getLegendRenderType() == LegendRenderType.Line) {
+ markerWidth = chart.getStyler().getLegendSeriesLineLength();
+ }
+ float legendEntryWidth = getLegendEntryWidth(seriesTextBounds, markerWidth);
+ startx += legendEntryWidth + chart.getStyler().getLegendPadding();
+ }
+ }
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldHint);
+ }
+
+ @Override
+ public double getSeriesLegendRenderGraphicHeight(S series) {
+
+ return BOX_SIZE;
+ }
+}
diff --git a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/PlotContent_.java b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/PlotContent_.java
index 16e43f76..0f64ac0a 100644
--- a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/PlotContent_.java
+++ b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/PlotContent_.java
@@ -12,7 +12,7 @@
public abstract class PlotContent_ implements ChartPart {
final Chart chart;
- ToolTips toolTips; // tooltips are available for Category, OHLC and XY charts
+ ToolTips toolTips; // tooltips are available for Category, HorizontalBar, OHLC and XY charts
ChartZoom chartZoom;
// Cursor cursor;
diff --git a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/PlotContent_HorizontalBar.java b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/PlotContent_HorizontalBar.java
new file mode 100644
index 00000000..75d4a60c
--- /dev/null
+++ b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/PlotContent_HorizontalBar.java
@@ -0,0 +1,255 @@
+package org.knowm.xchart.internal.chartpart;
+
+import java.awt.*;
+import java.awt.font.FontRenderContext;
+import java.awt.font.TextLayout;
+import java.awt.geom.*;
+import java.util.*;
+import org.knowm.xchart.HorizontalBarSeries;
+import org.knowm.xchart.internal.Utils;
+import org.knowm.xchart.style.HorizontalBarStyler;
+
+public class PlotContent_HorizontalBar<
+ ST extends HorizontalBarStyler, S extends HorizontalBarSeries>
+ extends PlotContent_ {
+
+ private final ST styler;
+
+ /**
+ * Constructor
+ *
+ * @param chart
+ */
+ PlotContent_HorizontalBar(Chart chart) {
+
+ super(chart);
+ this.styler = chart.getStyler();
+ }
+
+ @Override
+ public void doPaint(Graphics2D g) {
+
+ // X-Axis
+ double yTickSpace = styler.getPlotContentSize() * getBounds().getHeight();
+ // System.out.println("xTickSpace: " + xTickSpace);
+ double yVerticalMargin = Utils.getTickStartOffset(getBounds().getHeight(), yTickSpace);
+ // System.out.println("xLeftMargin: " + xLeftMargin);
+ Map seriesMap = chart.getSeriesMap();
+ int numCategories = seriesMap.values().iterator().next().getYData().size();
+ double gridStep = yTickSpace / numCategories;
+ // System.out.println("gridStep: " + gridStep);
+
+ // Y-Axis
+ double xMin = chart.getXAxis().getMin();
+ double xMax = chart.getXAxis().getMax();
+
+ // figure out the general form of the chart
+ final int chartForm; // 1=positive, -1=negative, 0=span
+ if (xMin > 0.0 && xMax > 0.0) {
+ chartForm = 1; // positive chart
+ } else if (xMin < 0.0 && xMax < 0.0) {
+ chartForm = -1; // negative chart
+ } else {
+ chartForm = 0; // span chart
+ }
+ // System.out.println(xMin);
+ // System.out.println(xMax);
+ // System.out.println("chartForm: " + chartForm);
+
+ double xTickSpace = styler.getPlotContentSize() * getBounds().getWidth();
+
+ double xHorizontalMargin = Utils.getTickStartOffset(getBounds().getWidth(), xTickSpace);
+
+ // plot series
+ int seriesCounter = 0;
+
+ for (S series : seriesMap.values()) {
+
+ if (!series.isEnabled()) {
+ continue;
+ }
+
+ xMin = chart.getXAxis().getMin();
+ xMax = chart.getXAxis().getMax();
+ if (styler.isXAxisLogarithmic()) {
+ xMin = Math.log10(xMin);
+ xMax = Math.log10(xMax);
+ }
+
+ Iterator extends Number> xItr = series.getXData().iterator();
+ Iterator> yItr = series.getYData().iterator();
+
+ int categoryCounter = 0;
+ while (xItr.hasNext()) {
+
+ Number next = xItr.next();
+ Object nextCat = yItr.next();
+ // skip when a value is null
+ if (next == null) {
+
+ categoryCounter++;
+ continue;
+ }
+
+ double xOrig = next.doubleValue();
+ double x;
+ if (styler.isXAxisLogarithmic()) {
+ x = Math.log10(xOrig);
+ } else {
+ x = xOrig;
+ }
+
+ double xRight = 0.0;
+ double xLeft = 0.0;
+ switch (chartForm) {
+ case 1: // positive chart
+ // check for points off the chart draw area due to a custom yMin
+ if (x < xMin) {
+ categoryCounter++;
+ continue;
+ }
+ xRight = x;
+ xLeft = xMin;
+ break;
+
+ case -1: // negative chart
+ // check for points off the chart draw area due to a custom yMin
+ if (x > xMax) {
+ categoryCounter++;
+ continue;
+ }
+ xRight = xMax;
+ xLeft = x;
+ break;
+ case 0: // span chart
+ if (x >= 0.0) { // positive
+ xRight = x;
+ xLeft = 0.0;
+ } else {
+ xRight = 0.0;
+ xLeft = x;
+ }
+ break;
+ default:
+ break;
+ }
+
+ double xTransform = (xHorizontalMargin + (xRight - xMin) / (xMax - xMin) * xTickSpace);
+ double xOffset = getBounds().getX() + xTransform;
+
+ double zeroTransform = (xHorizontalMargin + (xLeft - xMin) / (xMax - xMin) * xTickSpace);
+ double zeroOffset = getBounds().getX() + zeroTransform;
+ double yOffset;
+ double barHeight;
+
+ {
+ double barHeightPercentage = styler.getAvailableSpaceFill();
+ barHeight = gridStep / chart.getSeriesMap().size() * barHeightPercentage;
+ double barMargin = gridStep * (1 - barHeightPercentage) / 2;
+ yOffset =
+ getBounds().getY()
+ + yVerticalMargin
+ + gridStep * categoryCounter++
+ + seriesCounter * barHeight
+ + barMargin;
+ }
+
+ // paint series
+ // paint bar
+ Path2D.Double barPath = new Path2D.Double();
+ barPath.moveTo(zeroOffset, yOffset);
+ barPath.lineTo(xOffset, yOffset);
+ barPath.lineTo(xOffset, yOffset + barHeight);
+ barPath.lineTo(zeroOffset, yOffset + barHeight);
+ barPath.closePath();
+
+ g.setColor(series.getFillColor());
+ g.fill(barPath);
+
+ if (styler.isLabelsVisible() && next != null) {
+ drawLabels(g, next, xOffset, yOffset, zeroOffset, barHeight, series.getFillColor());
+ }
+
+ // add data labels
+ if (chart.getStyler().isToolTipsEnabled()) {
+ Rectangle2D.Double rect =
+ new Rectangle2D.Double(
+ zeroOffset, yOffset, Math.abs(xOffset - zeroOffset), barHeight);
+ double xPoint;
+ if (x < 0) {
+ xPoint = -zeroOffset;
+ } else {
+ xPoint = xOffset;
+ }
+
+ toolTips.addData(
+ rect,
+ xPoint,
+ yOffset,
+ barHeight,
+ chart.getXAxisFormat().format(xOrig),
+ chart.getYAxisFormat().format(nextCat));
+ }
+ }
+
+ seriesCounter++;
+ }
+ }
+
+ private void drawLabels(
+ Graphics2D g,
+ Number next,
+ double xOffset,
+ double yOffset,
+ double zeroOffset,
+ double barHeight,
+ Color seriesColor) {
+
+ String numberAsString = chart.getXAxisFormat().format(next);
+
+ TextLayout textLayout =
+ new TextLayout(
+ numberAsString, styler.getLabelsFont(), new FontRenderContext(null, true, false));
+
+ AffineTransform rot =
+ AffineTransform.getRotateInstance(-1 * Math.toRadians(styler.getLabelsRotation()), 0, 0);
+ Shape shape = textLayout.getOutline(rot);
+ Rectangle2D labelRectangle = textLayout.getBounds();
+
+ double labelY;
+ if (styler.getLabelsRotation() > 0) {
+ double labelYDelta = labelRectangle.getWidth() / 2 - labelRectangle.getHeight() / 2;
+ double rotationOffset = labelYDelta * styler.getLabelsRotation() / 90;
+ labelY = yOffset + barHeight / 2 + labelRectangle.getHeight() / 2 + rotationOffset + 1;
+ } else {
+ labelY = yOffset + barHeight / 2 + labelRectangle.getHeight() / 2;
+ }
+ double labelX;
+
+ if (next.doubleValue() >= 0.0) {
+ labelX =
+ xOffset
+ + (zeroOffset - xOffset) * (1 - styler.getLabelsPosition())
+ - labelRectangle.getWidth() * styler.getLabelsPosition();
+ } else {
+ labelX =
+ zeroOffset
+ - (zeroOffset - xOffset) * (1 - styler.getLabelsPosition())
+ - labelRectangle.getWidth() * (1 - styler.getLabelsPosition());
+ }
+
+ if (styler.isLabelsFontColorAutomaticEnabled()) {
+ g.setColor(styler.getLabelsFontColor(seriesColor));
+ } else {
+ g.setColor(styler.getLabelsFontColor());
+ }
+
+ g.setFont(styler.getLabelsFont());
+ AffineTransform orig = g.getTransform();
+ AffineTransform at = new AffineTransform();
+ at.translate(labelX, labelY);
+ g.transform(at);
+ g.fill(shape);
+ g.setTransform(orig);
+ }
+}
diff --git a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Plot_HorizontalBar.java b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Plot_HorizontalBar.java
new file mode 100644
index 00000000..68c3768e
--- /dev/null
+++ b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/Plot_HorizontalBar.java
@@ -0,0 +1,19 @@
+package org.knowm.xchart.internal.chartpart;
+
+import org.knowm.xchart.HorizontalBarSeries;
+import org.knowm.xchart.style.HorizontalBarStyler;
+
+public class Plot_HorizontalBar
+ extends Plot_AxesChart {
+
+ /**
+ * Constructor
+ *
+ * @param chart
+ */
+ public Plot_HorizontalBar(Chart chart) {
+
+ super(chart);
+ this.plotContent = new PlotContent_HorizontalBar(chart);
+ }
+}
diff --git a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/ToolTips.java b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/ToolTips.java
index 9fc4634a..8ec0eb5d 100644
--- a/xchart/src/main/java/org/knowm/xchart/internal/chartpart/ToolTips.java
+++ b/xchart/src/main/java/org/knowm/xchart/internal/chartpart/ToolTips.java
@@ -14,6 +14,7 @@
import java.util.List;
import java.util.Objects;
import org.knowm.xchart.style.BoxStyler;
+import org.knowm.xchart.style.HorizontalBarStyler;
import org.knowm.xchart.style.OHLCStyler;
import org.knowm.xchart.style.Styler;
@@ -169,8 +170,15 @@ private void paintToolTip(Graphics2D g, ToolTip tooltip) {
// System.out.println("topEdge = " + topEdge);
// System.out.println("bottomEdge = " + bottomEdge);
- double x = tooltip.x + tooltip.w / 2 - annotationRectangle.getWidth() / 2 - MARGIN;
- double y = tooltip.y - 3 * MARGIN - annotationRectangle.getHeight();
+ double x;
+ double y;
+ if (styler instanceof HorizontalBarStyler) {
+ x = tooltip.x < 0 ? -tooltip.x - w - MARGIN : tooltip.x + MARGIN;
+ y = tooltip.y + tooltip.w / 2 - h / 2;
+ } else {
+ x = tooltip.x + tooltip.w / 2 - annotationRectangle.getWidth() / 2 - MARGIN;
+ y = tooltip.y - 3 * MARGIN - annotationRectangle.getHeight();
+ }
// System.out.println("x = " + x);
// System.out.println("y = " + y);
// x = Math.min(x, -w);
diff --git a/xchart/src/main/java/org/knowm/xchart/style/HorizontalBarStyler.java b/xchart/src/main/java/org/knowm/xchart/style/HorizontalBarStyler.java
new file mode 100644
index 00000000..91f81b7f
--- /dev/null
+++ b/xchart/src/main/java/org/knowm/xchart/style/HorizontalBarStyler.java
@@ -0,0 +1,188 @@
+package org.knowm.xchart.style;
+
+import java.awt.*;
+import org.knowm.xchart.style.colors.FontColorDetector;
+import org.knowm.xchart.style.theme.Theme;
+
+public class HorizontalBarStyler extends AxesChartStyler {
+
+ private double availableSpaceFill;
+
+ // labels //////////////////////
+ private boolean isLabelsVisible = false;
+ private Font labelsFont;
+ private Color labelsFontColor;
+ private int labelsRotation;
+ private double labelsPosition;
+ private boolean isLabelsFontColorAutomaticEnabled;
+ private Color labelsFontColorAutomaticLight;
+ private Color labelsFontColorAutomaticDark;
+
+ /** Constructor */
+ public HorizontalBarStyler() {
+
+ setAllStyles();
+ }
+
+ @Override
+ protected void setAllStyles() {
+
+ super.setAllStyles();
+
+ availableSpaceFill = theme.getAvailableSpaceFill();
+ isLabelsVisible = false;
+ labelsFont = theme.getBaseFont();
+ labelsFontColor = theme.getChartFontColor();
+ labelsRotation = 0;
+ labelsPosition = 0.5;
+ isLabelsFontColorAutomaticEnabled = theme.isLabelsFontColorAutomaticEnabled();
+ labelsFontColorAutomaticLight = theme.getLabelsFontColorAutomaticLight();
+ labelsFontColorAutomaticDark = theme.getLabelsFontColorAutomaticDark();
+ }
+
+ public double getAvailableSpaceFill() {
+
+ return availableSpaceFill;
+ }
+
+ /**
+ * Sets the available space for rendering each category as a percentage. For a bar chart with one
+ * series, it will be the width of the bar as a percentage of the maximum space alloted for the
+ * bar. If there are three series and three bars, the three bars will share the available space.
+ * This affects all category series render types, not only bar charts. Full width is 100%, i.e.
+ * 1.0
+ *
+ * @param availableSpaceFill
+ */
+ public HorizontalBarStyler setAvailableSpaceFill(double availableSpaceFill) {
+
+ this.availableSpaceFill = availableSpaceFill;
+ return this;
+ }
+
+ public boolean isLabelsVisible() {
+
+ return isLabelsVisible;
+ }
+
+ /**
+ * Sets if labels should be added to charts. Each chart type has a different annotation type
+ *
+ * @param labelsVisible
+ */
+ public HorizontalBarStyler setLabelsVisible(boolean labelsVisible) {
+
+ this.isLabelsVisible = labelsVisible;
+ return this;
+ }
+
+ public Font getLabelsFont() {
+
+ return labelsFont;
+ }
+
+ /**
+ * Sets the Font used for chart labels
+ *
+ * @param labelsFont
+ */
+ public HorizontalBarStyler setLabelsFont(Font labelsFont) {
+
+ this.labelsFont = labelsFont;
+ return this;
+ }
+
+ public Color getLabelsFontColor() {
+ return labelsFontColor;
+ }
+
+ public Color getLabelsFontColor(Color backgroundColor) {
+
+ return FontColorDetector.getAutomaticFontColor(
+ backgroundColor, labelsFontColorAutomaticDark, labelsFontColorAutomaticLight);
+ }
+
+ /**
+ * Sets the color of the Font used for chart labels
+ *
+ * @param labelsFontColor
+ */
+ public HorizontalBarStyler setLabelsFontColor(Color labelsFontColor) {
+ this.labelsFontColor = labelsFontColor;
+ return this;
+ }
+
+ public int getLabelsRotation() {
+ return labelsRotation;
+ }
+
+ /**
+ * Sets the rotation (in degrees) for chart labels.
+ *
+ * @param labelsRotation
+ */
+ public HorizontalBarStyler setLabelsRotation(int labelsRotation) {
+ this.labelsRotation = labelsRotation;
+ return this;
+ }
+
+ public double getLabelsPosition() {
+
+ return labelsPosition;
+ }
+
+ /**
+ * A number between 0 and 1 setting the vertical position of the data label. Default is 0.5
+ * placing it in the center.
+ *
+ * @param labelsPosition
+ * @return
+ */
+ public HorizontalBarStyler setLabelsPosition(double labelsPosition) {
+
+ if (labelsPosition < 0 || labelsPosition > 1) {
+ throw new IllegalArgumentException("Annotations position must between 0 and 1!!!");
+ }
+ this.labelsPosition = labelsPosition;
+ return this;
+ }
+
+ public boolean isLabelsFontColorAutomaticEnabled() {
+ return isLabelsFontColorAutomaticEnabled;
+ }
+
+ public HorizontalBarStyler setLabelsFontColorAutomaticEnabled(
+ boolean isLabelsFontColorAutomaticEnabled) {
+ this.isLabelsFontColorAutomaticEnabled = isLabelsFontColorAutomaticEnabled;
+ return this;
+ }
+
+ public Color getLabelsFontColorAutomaticLight() {
+ return labelsFontColorAutomaticLight;
+ }
+
+ public HorizontalBarStyler setLabelsFontColorAutomaticLight(Color labelsFontColorAutomaticLight) {
+ this.labelsFontColorAutomaticLight = labelsFontColorAutomaticLight;
+ return this;
+ }
+
+ public Color getLabelsFontColorAutomaticDark() {
+ return labelsFontColorAutomaticDark;
+ }
+
+ public HorizontalBarStyler setLabelsFontColorAutomaticDark(Color labelsFontColorAutomaticDark) {
+ this.labelsFontColorAutomaticDark = labelsFontColorAutomaticDark;
+ return this;
+ }
+
+ /**
+ * Set the theme the styler should use
+ *
+ * @param theme
+ */
+ public void setTheme(Theme theme) {
+
+ this.theme = theme;
+ setAllStyles();
+ }
+}