001: /***********************************************************************************************
002: * Copyright 2002 (C) Nathaniel G. Auvil. All Rights Reserved.
003: *
004: * Redistribution and use of this software and associated documentation ("Software"), with or
005: * without modification, are permitted provided that the following conditions are met:
006: *
007: * 1. Redistributions of source code must retain copyright statements and notices.
008: * Redistributions must also contain a copy of this document.
009: *
010: * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
011: * conditions and the following disclaimer in the documentation and/or other materials
012: * provided with the distribution.
013: *
014: * 3. The name "jCharts" or "Nathaniel G. Auvil" must not be used to endorse or promote
015: * products derived from this Software without prior written permission of Nathaniel G.
016: * Auvil. For written permission, please contact nathaniel_auvil@users.sourceforge.net
017: *
018: * 4. Products derived from this Software may not be called "jCharts" nor may "jCharts" appear
019: * in their names without prior written permission of Nathaniel G. Auvil. jCharts is a
020: * registered trademark of Nathaniel G. Auvil.
021: *
022: * 5. Due credit should be given to the jCharts Project (http://jcharts.sourceforge.net/).
023: *
024: * THIS SOFTWARE IS PROVIDED BY Nathaniel G. Auvil AND CONTRIBUTORS ``AS IS'' AND ANY
025: * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
026: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
027: * jCharts OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
028: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
029: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
030: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,STRICT LIABILITY, OR TORT
031: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
032: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
033: ************************************************************************************************/package org.krysalis.jcharts.axisChart;
034:
035: import java.awt.Graphics2D;
036: import java.awt.geom.Rectangle2D;
037:
038: import org.krysalis.jcharts.axisChart.customRenderers.axisValue.AxisValueRenderEvent;
039: import org.krysalis.jcharts.chartData.interfaces.IAxisChartDataSet;
040: import org.krysalis.jcharts.chartText.TextTagGroup;
041: import org.krysalis.jcharts.imageMap.RectMapArea;
042: import org.krysalis.jcharts.properties.BarChartProperties;
043:
044: /*****************************************************************************************
045: * @author Nathaniel Auvil
046: * @version $Id: BarChart.java,v 1.3 2004/05/27 02:15:19 nathaniel_auvil Exp $
047: ****************************************************************************************/
048: abstract class BarChart {
049:
050: /***************************************************************************************
051: * Draws the chart uses Rectangle2D......keep having rounding problems.
052: *
053: * @param axisChart
054: * @param iAxisChartDataSet
055: **************************************************************************************/
056: static void render(AxisChart axisChart,
057: IAxisChartDataSet iAxisChartDataSet) {
058: Graphics2D g2d = axisChart.getGraphics2D();
059: BarChartProperties barChartProperties = (BarChartProperties) iAxisChartDataSet
060: .getChartTypeProperties();
061: float barWidth;
062:
063: //---y axis position on screen to start drawing.
064: float startingX;
065: float startingY;
066: float width;
067: float height;
068:
069: if (axisChart.getAxisProperties().isPlotHorizontal()) {
070: barWidth = axisChart.getYAxis().getScalePixelWidth()
071: * barChartProperties.getPercentage();
072: startingX = axisChart.getXAxis().getZeroLineCoordinate();
073: startingY = axisChart.getYAxis().getLastTickY()
074: - (barWidth / 2);
075: width = 0;
076: height = barWidth;
077: Rectangle2D.Float rectangle = new Rectangle2D.Float(
078: startingX, startingY, width, height);
079:
080: BarChart.horizontalPlot(axisChart, iAxisChartDataSet,
081: barChartProperties, g2d, rectangle, startingX);
082: } else {
083: barWidth = axisChart.getXAxis().getScalePixelWidth()
084: * barChartProperties.getPercentage();
085: startingX = axisChart.getXAxis().getTickStart()
086: - (barWidth / 2);
087: startingY = axisChart.getYAxis().getZeroLineCoordinate();
088: width = barWidth;
089: height = 0;
090: Rectangle2D.Float rectangle = new Rectangle2D.Float(
091: startingX, startingY, width, height);
092:
093: BarChart.verticalPlot(axisChart, iAxisChartDataSet,
094: barChartProperties, g2d, rectangle, startingY);
095: }
096: }
097:
098: /***************************************************************************************
099: * @param axisChart
100: * @param iAxisChartDataSet
101: * @param barChartProperties
102: * @param g2d
103: * @param rectangle
104: * @param startingX
105: **************************************************************************************/
106: private static void horizontalPlot(AxisChart axisChart,
107: IAxisChartDataSet iAxisChartDataSet,
108: BarChartProperties barChartProperties, Graphics2D g2d,
109: Rectangle2D.Float rectangle, float startingX) {
110: int imageMapLabelIndex = axisChart.getYAxis()
111: .getNumberOfScaleItems() - 1;
112:
113: //---setup the total area rectangle
114: Rectangle2D.Float totalItemArea = new Rectangle2D.Float();
115: totalItemArea.y = axisChart.getYAxis().getOrigin()
116: - axisChart.getYAxis().getPixelLength() + 1;
117: totalItemArea.height = axisChart.getYAxis()
118: .getScalePixelWidth() - 1;
119: totalItemArea.x = axisChart.getXAxis().getOrigin() + 1;
120: totalItemArea.width = axisChart.getXAxis().getPixelLength() - 1;
121:
122: //---reuse the same Object for pre and post render events.
123: AxisValueRenderEvent axisValueRenderEvent = new AxisValueRenderEvent(
124: axisChart, iAxisChartDataSet, g2d, totalItemArea,
125: axisChart.getXAxis().getZeroLineCoordinate());
126:
127: //---there is only ever one data set for a regular bar chart
128: axisValueRenderEvent.setDataSetIndex(0);
129:
130: //LOOP
131: for (int i = 0; i < iAxisChartDataSet.getNumberOfDataItems(); i++) {
132: //---reset the paint as it might have changed for the outline drawing
133: g2d.setPaint(iAxisChartDataSet.getPaint(0));
134:
135: //---set values for the preRender event
136: axisValueRenderEvent.setValueX(axisChart.getXAxis()
137: .getZeroLineCoordinate());
138: axisValueRenderEvent.setValueY((float) rectangle
139: .getCenterY());
140: axisValueRenderEvent.setValueIndex(i);
141:
142: //---we want to do this regardless if we render an item
143: barChartProperties.firePreRender(axisValueRenderEvent);
144:
145: //---if value == 0 do not plot anything.
146: if (iAxisChartDataSet.getValue(0, i) != 0.0d) {
147: if (iAxisChartDataSet.getValue(0, i) < 0) {
148: rectangle.x = axisChart.getXAxis()
149: .computeAxisCoordinate(
150: axisChart.getXAxis().getOrigin(),
151: iAxisChartDataSet.getValue(0, i),
152: axisChart.getXAxis()
153: .getScaleCalculator()
154: .getMinValue());
155: rectangle.width = startingX - rectangle.x;
156:
157: //---set values for the postRender event
158: axisValueRenderEvent.setValueX(rectangle.x);
159: } else {
160: rectangle.x = startingX;
161: rectangle.width = BarChart
162: .computeScaleHeightOfValue(
163: iAxisChartDataSet.getValue(0, i),
164: axisChart.getXAxis()
165: .getOneUnitPixelSize());
166:
167: //---set values for the postRender event
168: axisValueRenderEvent.setValueX(rectangle.x
169: + rectangle.width);
170: }
171:
172: //---with a user defined scale, we could have non-zero data points with a height of zero.
173: if (rectangle.width != 0) {
174: g2d.fill(rectangle);
175:
176: if (barChartProperties.getShowOutlinesFlag()) {
177: barChartProperties.getBarOutlineStroke().draw(
178: g2d, rectangle);
179: }
180:
181: //---if we are generating an ImageMap, store the image coordinates
182: if (axisChart.getGenerateImageMapFlag()) {
183: RectMapArea rectMapArea = createImageMapArea(
184: axisChart.getYAxis()
185: .getAxisLabelsGroup(),
186: imageMapLabelIndex, rectangle,
187: iAxisChartDataSet.getValue(0, i),
188: iAxisChartDataSet.getLegendLabel(0));
189: axisChart.getImageMap().addImageMapArea(
190: rectMapArea);
191:
192: imageMapLabelIndex--;
193: }
194: }
195: } else {
196: //---else, value is zero.
197:
198: //---if we are generating an ImageMap, store the image coordinates
199: if (axisChart.getGenerateImageMapFlag()) {
200: //---check if zero scale line is visible
201: //---make sure zero is even visible on the scale.
202: //---If zero is not visible on scale nothing will be clickable for that item
203: if (axisChart.getXAxis().getScaleCalculator()
204: .getMaxValue() >= 0
205: && axisChart.getXAxis()
206: .getScaleCalculator().getMinValue() <= 0) {
207: rectangle.x = startingX;
208: rectangle.width = 1;
209:
210: RectMapArea rectMapArea = createImageMapArea(
211: axisChart.getYAxis()
212: .getAxisLabelsGroup(),
213: imageMapLabelIndex, rectangle,
214: iAxisChartDataSet.getValue(0, i),
215: iAxisChartDataSet.getLegendLabel(0));
216:
217: axisChart.getImageMap().addImageMapArea(
218: rectMapArea);
219: }
220:
221: imageMapLabelIndex--;
222: }
223: }
224:
225: //---notify everyone we just rendered
226: barChartProperties.firePostRender(axisValueRenderEvent);
227: totalItemArea.y += axisChart.getYAxis()
228: .getScalePixelWidth();
229:
230: rectangle.y += axisChart.getYAxis().getScalePixelWidth();
231: }
232: }
233:
234: /***************************************************************************************
235: * @param axisChart
236: * @param iAxisChartDataSet
237: * @param barChartProperties
238: * @param g2d
239: * @param rectangle
240: * @param startingY
241: **************************************************************************************/
242: private static void verticalPlot(AxisChart axisChart,
243: IAxisChartDataSet iAxisChartDataSet,
244: BarChartProperties barChartProperties, Graphics2D g2d,
245: Rectangle2D.Float rectangle, float startingY) {
246: //---setup the total area rectangle
247: Rectangle2D.Float totalItemArea = new Rectangle2D.Float();
248: totalItemArea.x = axisChart.getXAxis().getOrigin() + 1;
249: totalItemArea.y = axisChart.getYAxis().getOrigin()
250: - axisChart.getYAxis().getPixelLength() + 1;
251: totalItemArea.width = axisChart.getXAxis().getScalePixelWidth() - 1;
252: totalItemArea.height = axisChart.getYAxis().getPixelLength() - 1;
253:
254: //---reuse the same Object for pre and post render events.
255: AxisValueRenderEvent axisValueRenderEvent = new AxisValueRenderEvent(
256: axisChart, iAxisChartDataSet, g2d, totalItemArea,
257: axisChart.getYAxis().getZeroLineCoordinate());
258:
259: //---there is only ever one data set for a regular bar chart
260: axisValueRenderEvent.setDataSetIndex(0);
261:
262: //LOOP
263: for (int i = 0; i < iAxisChartDataSet.getNumberOfDataItems(); i++) {
264: //---reset the paint as it might have changed for the outline drawing
265: g2d.setPaint(iAxisChartDataSet.getPaint(0));
266:
267: //---set values for the preRender event
268: axisValueRenderEvent.setValueX((float) rectangle
269: .getCenterX());
270: axisValueRenderEvent.setValueY(axisChart.getYAxis()
271: .getZeroLineCoordinate());
272: axisValueRenderEvent.setValueIndex(i);
273:
274: //---we want to do this regardless if we render an item
275: barChartProperties.firePreRender(axisValueRenderEvent);
276:
277: //---if value == 0 do not plot anything.
278: if (iAxisChartDataSet.getValue(0, i) != 0.0d) {
279: if (iAxisChartDataSet.getValue(0, i) < 0) {
280: rectangle.y = startingY;
281: rectangle.height = BarChart
282: .computeScaleHeightOfValue(
283: iAxisChartDataSet.getValue(0, i),
284: axisChart.getYAxis()
285: .getOneUnitPixelSize());
286:
287: axisValueRenderEvent.setValueY(rectangle.y
288: + rectangle.height);
289: } else {
290: rectangle.y = axisChart.getYAxis()
291: .computeAxisCoordinate(
292: axisChart.getYAxis().getOrigin(),
293: iAxisChartDataSet.getValue(0, i),
294: axisChart.getYAxis()
295: .getScaleCalculator()
296: .getMinValue());
297: rectangle.height = startingY - rectangle.y;
298:
299: axisValueRenderEvent.setValueY(rectangle.y);
300: }
301:
302: //---with a user defined scale, we could have non-zero data points with a height of zero.
303: if (rectangle.height != 0) {
304: g2d.fill(rectangle);
305:
306: if (barChartProperties.getShowOutlinesFlag()) {
307: barChartProperties.getBarOutlineStroke().draw(
308: g2d, rectangle);
309: g2d.setPaint(iAxisChartDataSet.getPaint(0));
310: }
311: }
312:
313: //---even if the bar is not visible, we still need to generate an ImageMapArea
314: if (axisChart.getGenerateImageMapFlag()) {
315: RectMapArea rectMapArea = createImageMapArea(
316: axisChart.getXAxis().getAxisLabelsGroup(),
317: i, rectangle, iAxisChartDataSet.getValue(0,
318: i), iAxisChartDataSet
319: .getLegendLabel(0));
320:
321: axisChart.getImageMap()
322: .addImageMapArea(rectMapArea);
323: }
324: } else {
325: //---if we are generating an ImageMap, store the image coordinates
326: if (axisChart.getGenerateImageMapFlag()) {
327: //---make sure zero is even visible on the scale.
328: //---If zero is not visible on scale nothing will be clickable for that item
329: if (axisChart.getYAxis().getScaleCalculator()
330: .getMaxValue() >= 0
331: && axisChart.getYAxis()
332: .getScaleCalculator().getMinValue() <= 0) {
333: rectangle.y = startingY;
334: rectangle.height = 1;
335:
336: RectMapArea rectMapArea = createImageMapArea(
337: axisChart.getXAxis()
338: .getAxisLabelsGroup(), i,
339: rectangle, iAxisChartDataSet.getValue(
340: 0, i), iAxisChartDataSet
341: .getLegendLabel(0));
342:
343: axisChart.getImageMap().addImageMapArea(
344: rectMapArea);
345: }
346: }
347: }
348:
349: //---notify everyone we just rendered
350: barChartProperties.firePostRender(axisValueRenderEvent);
351: totalItemArea.x += axisChart.getXAxis()
352: .getScalePixelWidth();
353:
354: rectangle.x += axisChart.getXAxis().getScalePixelWidth();
355: }
356: }
357:
358: /***************************************************************************************
359: * Takes a value and determines the number of pixels it should fill on the screen. If
360: * there is a user defined scale and the passed value is greater than the MAX or less
361: * than the MIN, the height will be forced to the MAX or MIN respectively.
362: *
363: * @param value
364: * @param oneUnitPixelSize
365: * @return float the screen pixel coordinate
366: **************************************************************************************/
367: static float computeScaleHeightOfValue(double value,
368: double oneUnitPixelSize) {
369: return (float) Math.abs((value) * oneUnitPixelSize);
370: }
371:
372: /***************************************************************************************
373: * @param axisLabelsGroup
374: * @param labelIndex
375: * @param rectangle
376: * @param value
377: * @param legendLabel
378: * @return RectMapArea
379: **************************************************************************************/
380: private static RectMapArea createImageMapArea(
381: TextTagGroup axisLabelsGroup, int labelIndex,
382: Rectangle2D.Float rectangle, double value,
383: String legendLabel) {
384: String label;
385: if (axisLabelsGroup != null) {
386: label = axisLabelsGroup.getTextTag(labelIndex).getText();
387: } else {
388: label = "";
389: }
390:
391: return new RectMapArea(rectangle, value, label, legendLabel);
392: }
393: }
|