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.axis;
034:
035: import org.krysalis.jcharts.axisChart.AxisChart;
036: import org.krysalis.jcharts.properties.*;
037: import org.krysalis.jcharts.test.HTMLGenerator;
038: import org.krysalis.jcharts.test.HTMLTestable;
039: import org.krysalis.jcharts.chartData.interfaces.IAxisPlotDataSet;
040: import org.krysalis.jcharts.chartData.interfaces.IAxisDataSeries;
041: import org.krysalis.jcharts.types.ChartType;
042:
043: import java.awt.*;
044: import java.awt.geom.Line2D;
045: import java.lang.reflect.Field;
046: import java.util.Iterator;
047:
048: /*************************************************************************************
049: *
050: * @author Nathaniel Auvil, John Thomson
051: * @version $Id: XAxis.java,v 1.3 2003/06/29 14:14:28 nathaniel_auvil Exp $
052: ************************************************************************************/
053: public class XAxis extends Axis implements HTMLTestable {
054: //---indicates which labels to display 1=every, 2=every other, 3=every third, etc...
055: private int xLabelFilter = 1;
056:
057: //---for some charts such as line, point, area, etc... we want to start plot at y-axis
058: private boolean startTicksAtAxis;
059:
060: /**************************************************************************************************
061: *
062: * @param axisChart
063: * @param numberOfScaleItems
064: ***************************************************************************************************/
065: public XAxis(AxisChart axisChart, int numberOfScaleItems) {
066: super (axisChart, numberOfScaleItems);
067: }
068:
069: /*************************************************************************************************
070: * Computes the minimum pixel height required for the X-Axis.
071: * Includes space, if needed, for: axis title + padding, axis values + tick padding, and tick marks.
072: *
073: * @param axisTitle
074: **************************************************************************************************/
075: public void computeMinimumHeightNeeded(String axisTitle) {
076: float heightNeeded = 0;
077: AxisProperties axisProperties = super .getAxisChart()
078: .getAxisProperties();
079: AxisTypeProperties axisTypeProperties = axisProperties
080: .getXAxisProperties();
081:
082: if (axisTypeProperties.showAxisLabels()) {
083: if (axisProperties.xAxisLabelsAreVertical()) {
084: //---widest label for vertical labels
085: //heightNeeded = axisChartDataProcessor.getAxisLabelProcessor().getWidestLabel();
086: heightNeeded = super .getAxisLabelsGroup()
087: .getWidestLabel();
088: } else {
089: //---tallest label for horizontal labels
090: //heightNeeded = axisChartDataProcessor.getAxisLabelProcessor().getTallestLabel();
091: heightNeeded = super .getAxisLabelsGroup()
092: .getTallestLabel();
093:
094: //---not sure why i need more padding
095: heightNeeded += 3;
096: }
097: }
098:
099: if (axisTypeProperties.getShowTicks() != AxisTypeProperties.TICKS_NONE) {
100: if (axisTypeProperties.showAxisLabels()) {
101: //---add the padding between scale labels and tick marks
102: heightNeeded += axisTypeProperties
103: .getPaddingBetweenLabelsAndTicks();
104: }
105:
106: //---add width of tick marks
107: heightNeeded += axisTypeProperties
108: .getAxisTickMarkPixelLength();
109: } else {
110: //---use specified distance between labels and axis
111: heightNeeded += axisTypeProperties
112: .getPaddingBetweenAxisAndLabels();
113: }
114:
115: //---include axis title height if needed. Remember it is vertical for y-axis
116: if (axisTitle != null) {
117: super .computeAxisTitleDimensions(axisTitle,
118: axisTypeProperties.getTitleChartFont());
119: heightNeeded += super .getTitleHeight();
120: heightNeeded += axisTypeProperties
121: .getPaddingBetweenAxisTitleAndLabels();
122: }
123:
124: super .setMinimumHeightNeeded(heightNeeded);
125: }
126:
127: /*************************************************************************************************
128: * Computes the number of pixels between each value on the axis.
129: *
130: **************************************************************************************************
131: public void computeScalePixelWidth( int numberOfValuesOnXAxis )
132: {
133: super.setScalePixelWidth( super.getPixelLength() / numberOfValuesOnXAxis );
134: }
135:
136:
137: /****************************************************************************************************
138: *
139: * @param axisTitle
140: * @param graphics2D
141: * @param axisTypeProperties
142: ***************************************************************************************************/
143: private void renderAxisTitle(String axisTitle,
144: Graphics2D graphics2D, AxisTypeProperties axisTypeProperties) {
145: if (axisTitle != null) {
146: float titleX;
147: float titleY = super .getAxisChart().getYAxis().getOrigin()
148: + this .getMinimumHeightNeeded()
149: - super .getTitleHeight();
150:
151: //---if title is larger than the axis itself, place at top.
152: if (super .getTitleWidth() > super .getPixelLength()) {
153: titleX = ((super .getAxisChart().getImageWidth() - super
154: .getTitleWidth()) / 2);
155: }
156: //---else, center on XAxis.
157: else {
158: titleX = super .getOrigin()
159: + ((super .getPixelLength() - super
160: .getTitleWidth()) / 2);
161: }
162:
163: axisTypeProperties.getAxisTitleChartFont().setupGraphics2D(
164: graphics2D);
165: graphics2D.drawString(axisTitle, titleX, titleY);
166: }
167: }
168:
169: /***************************************************************************************
170: * Determines if we should start x-axis ticks at the y-axis or space it out a half
171: * a scale item width.
172: *
173: * @param iAxisDataSeries
174: * @param axisTypeProperties
175: **************************************************************************************/
176: public void computeShouldTickStartAtYAxis(
177: IAxisDataSeries iAxisDataSeries,
178: AxisTypeProperties axisTypeProperties) {
179: //---if horizontal plot, x-axis is the data axis, so always start data points at y-axis
180: if (axisTypeProperties instanceof DataAxisProperties) {
181: this .startTicksAtAxis = true;
182: } else {
183: this .startTicksAtAxis = true;
184:
185: //---else, there are a couple of plots we do not start x-axis values at the y-axis
186: IAxisPlotDataSet iAxisPlotDataSet;
187: Iterator iterator = iAxisDataSeries
188: .getIAxisPlotDataSetIterator();
189: //todo what about combo charts?
190: while (iterator.hasNext()) {
191: iAxisPlotDataSet = (IAxisPlotDataSet) iterator.next();
192: if (iAxisPlotDataSet.getChartType().equals(
193: ChartType.BAR)
194: || iAxisPlotDataSet.getChartType().equals(
195: ChartType.BAR_CLUSTERED)
196: || iAxisPlotDataSet.getChartType().equals(
197: ChartType.BAR_STACKED)
198: || iAxisPlotDataSet.getChartType().equals(
199: ChartType.LINE)
200: || iAxisPlotDataSet.getChartType().equals(
201: ChartType.POINT)
202: || iAxisPlotDataSet.getChartType().equals(
203: ChartType.STOCK)) {
204: this .startTicksAtAxis = false;
205: break;
206: }
207: }
208: }
209: }
210:
211: /***************************************************************************************
212: * Computes the screen pixel location of the first tick mark
213: *
214: **************************************************************************************/
215: public void computeTickStart() {
216: float tickStart = super .getOrigin();
217:
218: if (!this .startTicksAtAxis) {
219: tickStart += (super .getScalePixelWidth() / 2);
220: }
221:
222: super .setTickStart(tickStart);
223: }
224:
225: /*************************************************************************************************
226: * Computes the number of pixels between each value on the axis.
227: *
228: * @param axisTypeProperties
229: *************************************************************************************************/
230: public void computeScalePixelWidth(
231: AxisTypeProperties axisTypeProperties) {
232: if (this .startTicksAtAxis) {
233: super .computeScalePixelWidthDataAxis(axisTypeProperties);
234: } else {
235: super .setScalePixelWidth(getPixelLength()
236: / this .getNumberOfScaleItems());
237: }
238: }
239:
240: /*********************************************************************************************
241: * Renders the YAxis on the passes Graphics2D object
242: *
243: * @param graphics2D
244: * @param axisProperties
245: * @param axisTitle
246: **********************************************************************************************/
247: public void render(Graphics2D graphics2D,
248: AxisProperties axisProperties, String axisTitle) {
249: AxisTypeProperties axisTypeProperties = axisProperties
250: .getXAxisProperties();
251:
252: //---AXIS TITLE
253: this .renderAxisTitle(axisTitle, graphics2D, axisTypeProperties);
254:
255: Line2D.Float line2D = new Line2D.Float(super .getTickStart(),
256: 0.0f, super .getTickStart(), 0.0f);
257: float tickY1 = super .getAxisChart().getYAxis().getOrigin();
258: float tickY2 = super .getAxisChart().getYAxis().getOrigin()
259: + axisTypeProperties.getAxisTickMarkPixelLength();
260: float gridLineY1 = super .getAxisChart().getYAxis().getOrigin();
261: float gridLineY2 = super .getAxisChart().getYAxis().getOrigin()
262: - super .getAxisChart().getYAxis().getPixelLength();
263:
264: float stringX = super .getTickStart();
265: float stringY = super .getAxisChart().getYAxis().getOrigin();
266: if (axisTypeProperties.getShowTicks() != AxisTypeProperties.TICKS_NONE) {
267: stringY += axisTypeProperties.getAxisTickMarkPixelLength()
268: + axisTypeProperties
269: .getPaddingBetweenLabelsAndTicks();
270: } else {
271: stringY += axisTypeProperties
272: .getPaddingBetweenAxisAndLabels();
273: }
274:
275: if (axisTypeProperties.showAxisLabels()) {
276: //---if the scale labels are horizontal, simply add the tallest label height.
277: //---Otherwise we will have to calculate it when we draw the label
278: if (!axisProperties.xAxisLabelsAreVertical()) {
279: stringY += super .getAxisLabelsGroup().getTallestLabel();
280: graphics2D.setFont(axisTypeProperties
281: .getScaleChartFont().getFont());
282: } else {
283: stringX -= super .getAxisLabelsGroup().getTextTag(0)
284: .getFontDescent();
285: graphics2D.setFont(axisTypeProperties
286: .getScaleChartFont().deriveFont());
287: }
288: }
289:
290: //LOOP
291: //for( int i = 0; i < super.getAxisLabelsGroup().size(); i++ )
292: for (int i = 0; i < super .getNumberOfScaleItems(); i++) {
293: //---GRID LINES
294: if (axisTypeProperties.getShowGridLines() != AxisTypeProperties.GRID_LINES_NONE) {
295: if ((i == 0 && !(axisTypeProperties instanceof DataAxisProperties))
296: || (i > 0 && ((axisTypeProperties
297: .getShowGridLines() == AxisTypeProperties.GRID_LINES_ALL) || (axisTypeProperties
298: .getShowGridLines() == AxisTypeProperties.GRID_LINES_ONLY_WITH_LABELS && (i
299: % this .xLabelFilter == 0))))) {
300: line2D.y1 = gridLineY1;
301: line2D.y2 = gridLineY2;
302:
303: if (i < super .getAxisLabelsGroup().size()
304: || (i == super .getAxisLabelsGroup().size() && !axisTypeProperties
305: .getShowEndBorder())) {
306: axisTypeProperties.getGridLineChartStroke()
307: .draw(graphics2D, line2D);
308: }
309: }
310: }
311:
312: //---TICK MARKS
313: //if( i != super.getAxisLabelsGroup().size() )
314: if (i != super .getNumberOfScaleItems()) {
315: if ((axisTypeProperties.getShowTicks() == AxisTypeProperties.TICKS_ALL)
316: || (axisTypeProperties.getShowTicks() == AxisTypeProperties.TICKS_ONLY_WITH_LABELS && (i
317: % this .xLabelFilter == 0))) {
318: line2D.y1 = tickY1;
319: line2D.y2 = tickY2;
320:
321: axisTypeProperties.getTickChartStroke()
322: .setupGraphics2D(graphics2D);
323: graphics2D.draw(line2D);
324: }
325: }
326:
327: line2D.x1 += super .getScalePixelWidth();
328: line2D.x2 = line2D.x1;
329:
330: //---AXIS LABEL
331: if (axisTypeProperties.showAxisLabels()
332: && (i % this .xLabelFilter == 0)) {
333: graphics2D.setPaint(axisTypeProperties
334: .getScaleChartFont().getPaint());
335:
336: if (!axisProperties.xAxisLabelsAreVertical()) {
337: //graphics2D.drawString( iDataSeries.getXAxisLabel( i ), stringX - super.getAxisLabelsGroup().getTextTag( i ).getWidth() / 2, stringY );
338: float x = stringX
339: - super .getAxisLabelsGroup().getTextTag(i)
340: .getWidth() / 2;
341:
342: //---we can not only look at the last label as there could be a filter and labels near the last might go off the edge of the screen.
343: if (x
344: + super .getAxisLabelsGroup().getTextTag(i)
345: .getWidth() < super .getAxisChart()
346: .getImageWidth()) {
347: super .getAxisLabelsGroup().getTextTag(i)
348: .render(graphics2D, x, stringY);
349: }
350: } else {
351: float x = stringX
352: + super .getAxisLabelsGroup().getTextTag(i)
353: .getHeight() / 2;
354:
355: //---we can not only look at the last label as there could be a filter and labels near the last might go off the edge of the screen.
356: if (x
357: + super .getAxisLabelsGroup().getTextTag(i)
358: .getHeight() < super .getAxisChart()
359: .getImageWidth()) {
360: graphics2D.drawString(super
361: .getAxisLabelsGroup().getTextTag(i)
362: .getText(), x, stringY
363: + super .getAxisLabelsGroup()
364: .getTextTag(i).getWidth());
365: }
366: }
367: }
368: stringX += super .getScalePixelWidth();
369: }
370:
371: //---RIGHT BORDER-----------------------------------------------------------
372: if (axisTypeProperties.getShowEndBorder()) {
373: //---make sure no rounding errors
374: line2D.x1 = super .getOrigin() + super .getPixelLength();
375: line2D.x2 = line2D.x1;
376: line2D.y1 = gridLineY1;
377: line2D.y2 = gridLineY2;
378: axisProperties.getYAxisProperties().getAxisStroke().draw(
379: graphics2D, line2D);
380: }
381:
382: //---AXIS-------------------------------------------------------------------
383: line2D.x1 = super .getOrigin();
384: line2D.x2 = super .getOrigin() + super .getPixelLength();
385: line2D.y1 = super .getAxisChart().getYAxis().getOrigin();
386: line2D.y2 = line2D.y1;
387: axisTypeProperties.getAxisStroke().setupGraphics2D(graphics2D);
388: graphics2D.draw(line2D);
389:
390: //---ZERO LINE-----------------------------------------------------------------
391: if (axisTypeProperties instanceof DataAxisProperties) {
392: DataAxisProperties dataAxisProperties = (DataAxisProperties) axisTypeProperties;
393:
394: if (dataAxisProperties.showZeroLine()
395: && super .getScaleCalculator().getMinValue() < 0.0d
396: && super .getScaleCalculator().getMaxValue() > 0.0d) {
397: line2D.x1 = super .getZeroLineCoordinate();
398: line2D.x2 = line2D.x1;
399: line2D.y1 = super .getAxisChart().getYAxis().getOrigin();
400: line2D.y2 = super .getAxisChart().getYAxis().getOrigin()
401: - super .getAxisChart().getYAxis()
402: .getPixelLength();
403: dataAxisProperties.getZeroLineChartStroke().draw(
404: graphics2D, line2D);
405: }
406: }
407: }
408:
409: /************************************************************************************************
410: * Method to compute the filter to use on the x-axis label display so labels do not overlap
411: *
412: *************************************************************************************************/
413: public void computeLabelFilter() {
414: if (super .getAxisChart().getAxisProperties()
415: .getXAxisProperties().showAxisLabels()) {
416: float widestLabelSize;
417: AxisTypeProperties axisTypeProperties = super
418: .getAxisChart().getAxisProperties()
419: .getXAxisProperties();
420:
421: if (super .getAxisChart().getAxisProperties()
422: .xAxisLabelsAreVertical()) {
423: widestLabelSize = super .getAxisLabelsGroup()
424: .getTallestLabel();
425: } else {
426: widestLabelSize = super .getAxisLabelsGroup()
427: .getWidestLabel();
428: }
429:
430: double numberLabelsCanDisplay = this .getPixelLength()
431: / (widestLabelSize + axisTypeProperties
432: .getPaddingBetweenAxisLabels());
433: this .xLabelFilter = (int) Math.ceil(super
434: .getAxisLabelsGroup().size()
435: / numberLabelsCanDisplay);
436: } else {
437: this .xLabelFilter = 1;
438: }
439: }
440:
441: /*********************************************************************************************
442: * Enables the testing routines to display the contents of this Object.
443: *
444: * @param htmlGenerator
445: **********************************************************************************************/
446: public void toHTML(HTMLGenerator htmlGenerator) {
447: htmlGenerator.propertiesTableStart(this .getClass().getName());
448:
449: super .toHTML(htmlGenerator);
450:
451: Field[] fields = this .getClass().getDeclaredFields();
452: for (int i = 0; i < fields.length; i++) {
453: try {
454: htmlGenerator.addField(fields[i].getName(), fields[i]
455: .get(this ));
456: } catch (IllegalAccessException illegalAccessException) {
457: illegalAccessException.printStackTrace();
458: }
459: }
460:
461: htmlGenerator.propertiesTableEnd();
462: }
463:
464: /*************************************************************************************************
465: * Takes a value and determines the screen coordinate it should be drawn at. THe only difference
466: * between this and the y-axis is we add to the origin versus subtract from it.
467: *
468: * @param origin
469: * @param value
470: * @param axisMinValue the minimum value on the axis
471: * @return float the screen pixel coordinate
472: **************************************************************************************************/
473: public float computeAxisCoordinate(float origin, double value,
474: double axisMinValue) {
475: double returnValue = origin + (value - axisMinValue)
476: * this .getOneUnitPixelSize();
477: //System.out.println( "computeAxisCoordinate( " + origin + ", " + value + ", " + axisMinValue + " ) = " + returnValue );
478: return (float) returnValue;
479: }
480:
481: }
|