/*******************************************************************************
* 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 );
}
}