create changelog entry
[debian/openrocket] / android-libraries / achartengine / src / org / achartengine / chart / BarChart.java
1 /**\r
2  * Copyright (C) 2009 - 2012 SC 4ViewSoft SRL\r
3  *  \r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *  \r
8  *      http://www.apache.org/licenses/LICENSE-2.0\r
9  *  \r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 package org.achartengine.chart;\r
17 \r
18 import org.achartengine.model.XYMultipleSeriesDataset;\r
19 import org.achartengine.model.XYSeries;\r
20 import org.achartengine.renderer.SimpleSeriesRenderer;\r
21 import org.achartengine.renderer.XYMultipleSeriesRenderer;\r
22 \r
23 import android.graphics.Canvas;\r
24 import android.graphics.Color;\r
25 import android.graphics.Paint;\r
26 import android.graphics.Paint.Style;\r
27 import android.graphics.RectF;\r
28 import android.graphics.drawable.GradientDrawable;\r
29 import android.graphics.drawable.GradientDrawable.Orientation;\r
30 \r
31 /**\r
32  * The bar chart rendering class.\r
33  */\r
34 public class BarChart extends XYChart {\r
35   /** The constant to identify this chart type. */\r
36   public static final String TYPE = "Bar";\r
37   /** The legend shape width. */\r
38   private static final int SHAPE_WIDTH = 12;\r
39   /** The chart type. */\r
40   protected Type mType = Type.DEFAULT;\r
41 \r
42   /**\r
43    * The bar chart type enum.\r
44    */\r
45   public enum Type {\r
46     DEFAULT, STACKED;\r
47   }\r
48 \r
49   BarChart() {\r
50   }\r
51 \r
52   BarChart(Type type) {\r
53     mType = type;\r
54   }\r
55 \r
56   /**\r
57    * Builds a new bar chart instance.\r
58    * \r
59    * @param dataset the multiple series dataset\r
60    * @param renderer the multiple series renderer\r
61    * @param type the bar chart type\r
62    */\r
63   public BarChart(XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer, Type type) {\r
64     super(dataset, renderer);\r
65     mType = type;\r
66   }\r
67 \r
68   @Override\r
69   protected ClickableArea[] clickableAreasForPoints(float[] points, double[] values,\r
70       float yAxisValue, int seriesIndex, int startIndex) {\r
71     int seriesNr = mDataset.getSeriesCount();\r
72     int length = points.length;\r
73     ClickableArea[] ret = new ClickableArea[length / 2];\r
74     float halfDiffX = getHalfDiffX(points, length, seriesNr);\r
75     for (int i = 0; i < length; i += 2) {\r
76       float x = points[i];\r
77       float y = points[i + 1];\r
78       if (mType == Type.STACKED) {\r
79         ret[i / 2] = new ClickableArea(new RectF(x - halfDiffX, y, x + halfDiffX, yAxisValue),\r
80             values[i], values[i + 1]);\r
81       } else {\r
82         float startX = x - seriesNr * halfDiffX + seriesIndex * 2 * halfDiffX;\r
83         ret[i / 2] = new ClickableArea(new RectF(startX, y, startX + 2 * halfDiffX, yAxisValue),\r
84             values[i], values[i + 1]);\r
85       }\r
86     }\r
87     return ret;\r
88   }\r
89 \r
90   /**\r
91    * The graphical representation of a series.\r
92    * \r
93    * @param canvas the canvas to paint to\r
94    * @param paint the paint to be used for drawing\r
95    * @param points the array of points to be used for drawing the series\r
96    * @param seriesRenderer the series renderer\r
97    * @param yAxisValue the minimum value of the y axis\r
98    * @param seriesIndex the index of the series currently being drawn\r
99    * @param startIndex the start index of the rendering points\r
100    */\r
101   public void drawSeries(Canvas canvas, Paint paint, float[] points,\r
102       SimpleSeriesRenderer seriesRenderer, float yAxisValue, int seriesIndex, int startIndex) {\r
103     int seriesNr = mDataset.getSeriesCount();\r
104     int length = points.length;\r
105     paint.setColor(seriesRenderer.getColor());\r
106     paint.setStyle(Style.FILL);\r
107     float halfDiffX = getHalfDiffX(points, length, seriesNr);\r
108     for (int i = 0; i < length; i += 2) {\r
109       float x = points[i];\r
110       float y = points[i + 1];\r
111       drawBar(canvas, x, yAxisValue, x, y, halfDiffX, seriesNr, seriesIndex, paint);\r
112     }\r
113     paint.setColor(seriesRenderer.getColor());\r
114   }\r
115 \r
116   /**\r
117    * Draws a bar.\r
118    * \r
119    * @param canvas the canvas\r
120    * @param xMin the X axis minimum\r
121    * @param yMin the Y axis minimum\r
122    * @param xMax the X axis maximum\r
123    * @param yMax the Y axis maximum\r
124    * @param halfDiffX half the size of a bar\r
125    * @param seriesNr the total number of series\r
126    * @param seriesIndex the current series index\r
127    * @param paint the paint\r
128    */\r
129   protected void drawBar(Canvas canvas, float xMin, float yMin, float xMax, float yMax,\r
130       float halfDiffX, int seriesNr, int seriesIndex, Paint paint) {\r
131     int scale = mDataset.getSeriesAt(seriesIndex).getScaleNumber();\r
132     if (mType == Type.STACKED) {\r
133       drawBar(canvas, xMin - halfDiffX, yMax, xMax + halfDiffX, yMin, scale, seriesIndex, paint);\r
134     } else {\r
135       float startX = xMin - seriesNr * halfDiffX + seriesIndex * 2 * halfDiffX;\r
136       drawBar(canvas, startX, yMax, startX + 2 * halfDiffX, yMin, scale, seriesIndex, paint);\r
137     }\r
138   }\r
139 \r
140   /**\r
141    * Draws a bar.\r
142    * \r
143    * @param canvas the canvas\r
144    * @param xMin the X axis minimum\r
145    * @param yMin the Y axis minimum\r
146    * @param xMax the X axis maximum\r
147    * @param yMax the Y axis maximum\r
148    * @param scale the scale index\r
149    * @param seriesIndex the current series index\r
150    * @param paint the paint\r
151    */\r
152   private void drawBar(Canvas canvas, float xMin, float yMin, float xMax, float yMax, int scale,\r
153       int seriesIndex, Paint paint) {\r
154     SimpleSeriesRenderer renderer = mRenderer.getSeriesRendererAt(seriesIndex);\r
155     if (renderer.isGradientEnabled()) {\r
156       float minY = (float) toScreenPoint(new double[] { 0, renderer.getGradientStopValue() }, scale)[1];\r
157       float maxY = (float) toScreenPoint(new double[] { 0, renderer.getGradientStartValue() },\r
158           scale)[1];\r
159       float gradientMinY = Math.max(minY, Math.min(yMin, yMax));\r
160       float gradientMaxY = Math.min(maxY, Math.max(yMin, yMax));\r
161       int gradientMinColor = renderer.getGradientStopColor();\r
162       int gradientMaxColor = renderer.getGradientStartColor();\r
163       int gradientStartColor = gradientMaxColor;\r
164       int gradientStopColor = gradientMinColor;\r
165 \r
166       if (yMin < minY) {\r
167         paint.setColor(gradientMinColor);\r
168         canvas.drawRect(Math.round(xMin), Math.round(yMin), Math.round(xMax),\r
169             Math.round(gradientMinY), paint);\r
170       } else {\r
171         gradientStopColor = getGradientPartialColor(gradientMinColor, gradientMaxColor,\r
172             (maxY - gradientMinY) / (maxY - minY));\r
173       }\r
174       if (yMax > maxY) {\r
175         paint.setColor(gradientMaxColor);\r
176         canvas.drawRect(Math.round(xMin), Math.round(gradientMaxY), Math.round(xMax),\r
177             Math.round(yMax), paint);\r
178       } else {\r
179         gradientStartColor = getGradientPartialColor(gradientMaxColor, gradientMinColor,\r
180             (gradientMaxY - minY) / (maxY - minY));\r
181       }\r
182       GradientDrawable gradient = new GradientDrawable(Orientation.BOTTOM_TOP, new int[] {\r
183           gradientStartColor, gradientStopColor });\r
184       gradient.setBounds(Math.round(xMin), Math.round(gradientMinY), Math.round(xMax),\r
185           Math.round(gradientMaxY));\r
186       gradient.draw(canvas);\r
187     } else {\r
188       if (Math.abs(yMin - yMax) < 1) {\r
189         if (yMin < yMax) {\r
190           yMax = yMin + 1;\r
191         } else {\r
192           yMax = yMin - 1;\r
193         }\r
194       }\r
195       canvas\r
196           .drawRect(Math.round(xMin), Math.round(yMin), Math.round(xMax), Math.round(yMax), paint);\r
197     }\r
198   }\r
199 \r
200   private int getGradientPartialColor(int minColor, int maxColor, float fraction) {\r
201     int alpha = Math.round(fraction * Color.alpha(minColor) + (1 - fraction)\r
202         * Color.alpha(maxColor));\r
203     int r = Math.round(fraction * Color.red(minColor) + (1 - fraction) * Color.red(maxColor));\r
204     int g = Math.round(fraction * Color.green(minColor) + (1 - fraction) * Color.green(maxColor));\r
205     int b = Math.round(fraction * Color.blue(minColor) + (1 - fraction) * Color.blue((maxColor)));\r
206     return Color.argb(alpha, r, g, b);\r
207   }\r
208 \r
209   /**\r
210    * The graphical representation of the series values as text.\r
211    * \r
212    * @param canvas the canvas to paint to\r
213    * @param series the series to be painted\r
214    * @param renderer the series renderer\r
215    * @param paint the paint to be used for drawing\r
216    * @param points the array of points to be used for drawing the series\r
217    * @param seriesIndex the index of the series currently being drawn\r
218    * @param startIndex the start index of the rendering points\r
219    */\r
220   protected void drawChartValuesText(Canvas canvas, XYSeries series, SimpleSeriesRenderer renderer,\r
221       Paint paint, float[] points, int seriesIndex, int startIndex) {\r
222     int seriesNr = mDataset.getSeriesCount();\r
223     float halfDiffX = getHalfDiffX(points, points.length, seriesNr);\r
224     for (int i = 0; i < points.length; i += 2) {\r
225       int index = startIndex + i / 2;\r
226       double value = series.getY(index);\r
227       if (!isNullValue(value)) {\r
228         float x = points[i];\r
229         if (mType == Type.DEFAULT) {\r
230           x += seriesIndex * 2 * halfDiffX - (seriesNr - 1.5f) * halfDiffX;\r
231         }\r
232         if (value >= 0) {\r
233           drawText(canvas, getLabel(value), x, points[i + 1] - renderer.getChartValuesSpacing(),\r
234               paint, 0);\r
235         } else {\r
236           drawText(canvas, getLabel(value), x, points[i + 1] + renderer.getChartValuesTextSize()\r
237               + renderer.getChartValuesSpacing() - 3, paint, 0);\r
238         }\r
239       }\r
240     }\r
241   }\r
242 \r
243   /**\r
244    * Returns the legend shape width.\r
245    * \r
246    * @param seriesIndex the series index\r
247    * @return the legend shape width\r
248    */\r
249   public int getLegendShapeWidth(int seriesIndex) {\r
250     return SHAPE_WIDTH;\r
251   }\r
252 \r
253   /**\r
254    * The graphical representation of the legend shape.\r
255    * \r
256    * @param canvas the canvas to paint to\r
257    * @param renderer the series renderer\r
258    * @param x the x value of the point the shape should be drawn at\r
259    * @param y the y value of the point the shape should be drawn at\r
260    * @param seriesIndex the series index\r
261    * @param paint the paint to be used for drawing\r
262    */\r
263   public void drawLegendShape(Canvas canvas, SimpleSeriesRenderer renderer, float x, float y,\r
264       int seriesIndex, Paint paint) {\r
265     float halfShapeWidth = SHAPE_WIDTH / 2;\r
266     canvas.drawRect(x, y - halfShapeWidth, x + SHAPE_WIDTH, y + halfShapeWidth, paint);\r
267   }\r
268 \r
269   /**\r
270    * Calculates and returns the half-distance in the graphical representation of\r
271    * 2 consecutive points.\r
272    * \r
273    * @param points the points\r
274    * @param length the points length\r
275    * @param seriesNr the series number\r
276    * @return the calculated half-distance value\r
277    */\r
278   protected float getHalfDiffX(float[] points, int length, int seriesNr) {\r
279     int div = length;\r
280     if (length > 2) {\r
281       div = length - 2;\r
282     }\r
283     float halfDiffX = (points[length - 2] - points[0]) / div;\r
284     if (halfDiffX == 0) {\r
285       halfDiffX = 10;\r
286     }\r
287 \r
288     if (mType != Type.STACKED) {\r
289       halfDiffX /= seriesNr;\r
290     }\r
291     return (float) (halfDiffX / (getCoeficient() * (1 + mRenderer.getBarSpacing())));\r
292   }\r
293 \r
294   /**\r
295    * Returns the value of a constant used to calculate the half-distance.\r
296    * \r
297    * @return the constant value\r
298    */\r
299   protected float getCoeficient() {\r
300     return 1f;\r
301   }\r
302 \r
303   /**\r
304    * Returns if the chart should display the null values.\r
305    * \r
306    * @return if null values should be rendered\r
307    */\r
308   protected boolean isRenderNullValues() {\r
309     return true;\r
310   }\r
311 \r
312   /**\r
313    * Returns the default axis minimum.\r
314    * \r
315    * @return the default axis minimum\r
316    */\r
317   public double getDefaultMinimum() {\r
318     return 0;\r
319   }\r
320 \r
321   /**\r
322    * Returns the chart type identifier.\r
323    * \r
324    * @return the chart type\r
325    */\r
326   public String getChartType() {\r
327     return TYPE;\r
328   }\r
329 }\r