/******************************************************************************* * Copyright (c) 2008-2009 SWTChart project. All rights reserved. * * This code is distributed under the terms of the Eclipse Public License v1.0 * which is available at http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package org.swtchart.internal.series; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Display; import org.swtchart.Chart; import org.swtchart.IBarSeries; import org.swtchart.Range; import org.swtchart.IAxis.Direction; import org.swtchart.internal.axis.Axis; import org.swtchart.internal.compress.CompressBarSeries; import org.swtchart.internal.compress.CompressScatterSeries; /** * Bar series. */ public class BarSeries extends Series implements IBarSeries { /** the riser index in a category */ private int riserIndex; /** the riser color */ private Color barColor; /** the padding */ private int padding; /** the initial bar padding in percentage */ public static final int INITIAL_PADDING = 20; /** the alpha value */ private static final int ALPHA = 0xD0; /** the margin in pixels attached at the minimum/maximum plot */ private static final int MARGIN_AT_MIN_MAX_PLOT = 6; /** the default bar color */ private static final int DEFAULT_BAR_COLOR = SWT.COLOR_CYAN; /** * Constructor. * * @param chart * the chart * @param id * the series id */ protected BarSeries ( final Chart chart, final String id ) { super ( chart, id ); barColor = Display.getDefault ().getSystemColor ( DEFAULT_BAR_COLOR ); padding = INITIAL_PADDING; type = SeriesType.BAR; compressor = new CompressBarSeries (); } /* * @see IBarSeries#getBarPadding() */ public int getBarPadding () { return padding; } /* * @see IBarSeries#setBarPadding(int) */ public void setBarPadding ( final int padding ) { if ( ( padding < 0 ) || ( padding > 100 ) ) { SWT.error ( SWT.ERROR_INVALID_ARGUMENT ); } this.padding = padding; } /* * @see IBarSeries#getBarColor() */ public Color getBarColor () { if ( barColor.isDisposed () ) { barColor = Display.getDefault ().getSystemColor ( DEFAULT_BAR_COLOR ); } return barColor; } /* * @see IBarSeries#setBarColor(Color) */ public void setBarColor ( final Color color ) { if ( ( color != null ) && color.isDisposed () ) { SWT.error ( SWT.ERROR_INVALID_ARGUMENT ); } if ( color == null ) { barColor = Display.getDefault ().getSystemColor ( DEFAULT_BAR_COLOR ); } else { barColor = color; } } /* * @see IBarSeries#getBounds() */ public Rectangle[] getBounds () { Rectangle[] compressedBounds = getBoundsForCompressedSeries (); if ( ( (Axis)chart.getAxisSet ().getXAxis ( xAxisId ) ).isValidCategoryAxis () ) { return compressedBounds; } Rectangle[] rs = new Rectangle[xSeries.length]; double[] comporessedXSeries = compressor.getCompressedXSeries (); int cnt = 0; for ( int i = 0; i < xSeries.length; i++ ) { if ( ( cnt < comporessedXSeries.length ) && ( comporessedXSeries[cnt] == xSeries[i] ) ) { rs[i] = compressedBounds[cnt++]; } } return rs; } /** * Gets the array of bar rectangles for compressed series. * * @return the array of bar rectangles for compressed series */ private Rectangle[] getBoundsForCompressedSeries () { Axis xAxis = (Axis)chart.getAxisSet ().getXAxis ( xAxisId ); Axis yAxis = (Axis)chart.getAxisSet ().getYAxis ( yAxisId ); // get x and y series double[] xseries = compressor.getCompressedXSeries (); double[] yseries = compressor.getCompressedYSeries (); int[] indexes = compressor.getCompressedIndexes (); if ( xAxis.isValidCategoryAxis () ) { for ( int i = 0; i < xseries.length; i++ ) { xseries[i] = indexes[i]; } } Rectangle[] rectangles = new Rectangle[xseries.length]; Range xRange = xAxis.getRange (); Range yRange = yAxis.getRange (); for ( int i = 0; i < xseries.length; i++ ) { int x = xAxis.getPixelCoordinate ( xseries[i] ); int y = yAxis.getPixelCoordinate ( isValidStackSeries () ? stackSeries[indexes[i]] : yseries[i] ); double riserwidth = getRiserWidth ( xseries, i, xAxis, xRange.lower, xRange.upper ); double riserHeight = Math.abs ( yAxis.getPixelCoordinate ( yseries[i], yRange.lower, yRange.upper ) - yAxis .getPixelCoordinate ( yAxis.isLogScaleEnabled () ? yRange.lower : 0, yRange.lower, yRange.upper ) ); // adjust riser x coordinate and riser width for multiple series int riserCnt = xAxis.getNumRisers (); if ( riserCnt > 1 ) { if ( xAxis.isHorizontalAxis () ) { x = (int) ( x - riserwidth / 2d + riserwidth / riserCnt * ( riserIndex + 0.5 ) ); } else { x = (int) ( x - riserwidth / 2d + riserwidth / riserCnt * ( riserCnt - riserIndex - 0.5 ) ); } riserwidth /= riserCnt; } if ( xAxis.isHorizontalAxis () ) { // adjust coordinate for negative series if ( y > yAxis.getPixelCoordinate ( 0 ) ) { y = yAxis.getPixelCoordinate ( 0 ); } int width = (int)Math.ceil ( riserwidth ); width = ( width == 0 ) ? 1 : width; rectangles[i] = new Rectangle ( (int)Math.floor ( x - riserwidth / 2d ), y, width, (int)riserHeight ); } else { // adjust coordinate for negative series if ( y < yAxis.getPixelCoordinate ( 0 ) ) { y = yAxis.getPixelCoordinate ( 0 ); } int height = (int)Math.ceil ( riserwidth ); height = ( height == 0 ) ? 1 : height; rectangles[i] = new Rectangle ( (int) ( y - riserHeight ), (int)Math.floor ( x - riserwidth / 2d ), (int)riserHeight, height ); } } return rectangles; } /** * Sets the index of riser in a category. * * @param riserIndex * the index of riser in a category */ protected void setRiserIndex ( final int riserIndex ) { this.riserIndex = riserIndex; } /* * @see Series#setCompressor() */ @Override protected void setCompressor () { if ( isXMonotoneIncreasing ) { compressor = new CompressBarSeries (); } else { compressor = new CompressScatterSeries (); } } /* * @see Series#getAdjustedRange(Axis, int) */ @Override public Range getAdjustedRange ( final Axis axis, final int length ) { // calculate a range which has margin Range range; int lowerPlotMargin; int upperPlotMargin; if ( axis.getDirection () == Direction.X ) { double lowerRiserWidth = getRiserWidth ( xSeries, 0, axis, minX, maxX ); double upperRiserWidth = getRiserWidth ( xSeries, xSeries.length - 1, axis, minX, maxX ); lowerPlotMargin = (int) ( lowerRiserWidth / 2d + MARGIN_AT_MIN_MAX_PLOT ); upperPlotMargin = (int) ( upperRiserWidth / 2d + MARGIN_AT_MIN_MAX_PLOT ); range = getXRange (); } else { range = getYRange (); if ( range.upper < 0 ) { range.upper = 0; } if ( range.lower > 0 ) { range.lower = axis.isLogScaleEnabled () ? minY : 0; } lowerPlotMargin = ( range.lower == 0 ) ? 0 : MARGIN_AT_MIN_MAX_PLOT; upperPlotMargin = ( range.upper == 0 ) ? 0 : MARGIN_AT_MIN_MAX_PLOT; } return getRangeWithMargin ( lowerPlotMargin, upperPlotMargin, length, axis, range ); } /** * Gets the riser width. * * @param series * the X series * @param index * the series index * @param xAxis * the X axis * @param min * the min value of range * @param max * the max value of range * @return the raiser width in pixels */ private int getRiserWidth ( final double[] series, final int index, final Axis xAxis, final double min, final double max ) { // get two x coordinates double upper; double lower; if ( series.length == 1 ) { upper = series[0] + 0.5; lower = series[0] - 0.5; } else if ( ( index != series.length - 1 ) && ( ( index == 0 ) || ( series[index + 1] - series[index] < series[index] - series[index - 1] ) ) ) { upper = series[index + 1]; lower = series[index]; } else { upper = series[index]; lower = series[index - 1]; } // get riser width without padding int width = Math.abs ( xAxis.getPixelCoordinate ( upper, min, max ) - xAxis .getPixelCoordinate ( lower, min, max ) ); // adjust for padding width *= ( 100 - padding ) / 100d; // symbol size should be at least more than 1 if ( width == 0 ) { width = 1; } return width; } /** * Gets the color for riser frame. The color will be darker or lighter than * the given color. * * @param color * the riser color * @return the riser frame color */ protected Color getFrameColor ( final Color color ) { // int red = color.getRed (); // int green = color.getGreen (); // int blue = color.getBlue (); // // red *= ( red > 128 ) ? 0.8 : 1.2; // green *= ( green > 128 ) ? 0.8 : 1.2; // blue *= ( blue > 128 ) ? 0.8 : 1.2; // // return new Color ( color.getDevice (), red, green, blue ); return new Color ( color.getDevice (), color.getRGB () ); } /* * @see Series#draw(GC, int, int, Axis, Axis) */ @Override protected void draw ( final GC gc, final int width, final int height, final Axis xAxis, final Axis yAxis ) { // draw riser Rectangle[] rs = getBoundsForCompressedSeries (); for ( int i = 0; i < rs.length; i++ ) { drawRiser ( gc, rs[i].x, rs[i].y, rs[i].width, rs[i].height ); } // draw label and error bars if ( seriesLabel.isVisible () || xErrorBar.isVisible () || yErrorBar.isVisible () ) { double[] yseries = compressor.getCompressedYSeries (); int[] indexes = compressor.getCompressedIndexes (); for ( int i = 0; i < rs.length; i++ ) { seriesLabel .draw ( gc, rs[i].x + rs[i].width / 2, rs[i].y + rs[i].height / 2, yseries[i], indexes[i], SWT.CENTER ); int h, v; if ( xAxis.isHorizontalAxis () ) { h = xAxis.getPixelCoordinate ( xSeries[indexes[i]] ); v = yAxis.getPixelCoordinate ( ySeries[indexes[i]] ); } else { v = xAxis.getPixelCoordinate ( xSeries[indexes[i]] ); h = yAxis.getPixelCoordinate ( ySeries[indexes[i]] ); } xErrorBar.draw ( gc, h, v, xAxis, indexes[i] ); yErrorBar.draw ( gc, h, v, yAxis, indexes[i] ); } } } /** * Draws riser. * * @param gc * the graphics context * @param h * the horizontal coordinate * @param v * the vertical coordinate * @param width * the riser width * @param height * the riser height */ private void drawRiser ( final GC gc, final int h, final int v, final int width, final int height ) { int alpha = gc.getAlpha (); gc.setAlpha ( ALPHA ); gc.setBackground ( getBarColor () ); gc.fillRectangle ( h, v, width, height ); gc.setLineStyle ( SWT.LINE_SOLID ); Color frameColor = getFrameColor ( getBarColor () ); gc.setForeground ( frameColor ); gc.drawRectangle ( h, v, width, height ); frameColor.dispose (); gc.setAlpha ( alpha ); } }